Các lớp cơ sở trừu tượng và bản sao xây dựng, quy tắc của ngón tay cái


9

Thông thường, nên có một lớp cơ sở trừu tượng để cô lập giao diện của đối tượng.

Vấn đề là việc xây dựng bản sao, IMHO, bị phá vỡ khá nhiều theo mặc định trong C ++, với các hàm tạo sao chép được tạo theo mặc định.

Vì vậy, các vấn đề là gì khi bạn có một lớp cơ sở trừu tượng và con trỏ thô trong các lớp dẫn xuất?

class IAbstract
{
    ~IAbstract() = 0;
}

class Derived : public IAbstract
{
    char *theProblem;
    ...
}

IAbstract *a1 = new Derived();
IAbstract a2 = *a1;//???

Và bây giờ bạn có vô hiệu hóa cấu trúc sao chép cho toàn bộ phân cấp không? Khai báo bản sao xây dựng là tư nhân trong IAbstract?

Có bất kỳ quy tắc của ba với các lớp cơ sở trừu tượng?


1
sử dụng tài liệu tham khảo thay vì con trỏ :)
tp1

@ tp1: hoặc một số con trỏ thông minh ít nhất.
Benjamin Bannier

1
Đôi khi bạn chỉ cần làm việc với mã hiện có ... Bạn không thể thay đổi mọi thứ ngay lập tức.
Coder

Tại sao bạn nghĩ rằng trình xây dựng sao chép mặc định bị hỏng?
Bовић

2
@Coder: Hướng dẫn phong cách Google là một đống rác và hoàn toàn hút cho bất kỳ sự phát triển C ++ nào.
DeadMG

Câu trả lời:


6

Xây dựng bản sao trên một lớp trừu tượng nên được đặt ở chế độ riêng tư trong hầu hết các trường hợp, cũng như toán tử gán.

Các lớp trừu tượng, theo định nghĩa, được tạo thành một loại đa hình. Vì vậy, bạn không biết bao nhiêu bộ nhớ của bạn đang sử dụng và do đó không thể sao chép hoặc gán nó một cách an toàn. Trong thực tế, bạn có nguy cơ cắt lát: /programming/274626/what-is-the-slicing-pro Hiệu-in-c

Kiểu đa hình, trong C ++, không được thao tác theo giá trị. Bạn thao tác chúng bằng cách tham chiếu hoặc bằng con trỏ (hoặc bất kỳ con trỏ thông minh nào).

Đây là lý do tại sao Java đã làm cho đối tượng có thể thao tác bằng cách chỉ tham chiếu và tại sao C # và D có sự tách biệt giữa các lớp và cấu trúc (loại thứ nhất là loại đa hình và loại tham chiếu, loại thứ hai là loại không đa hình và loại giá trị).


2
Mọi thứ trong Java không tốt hơn. Trong Java, thật khó để sao chép bất cứ điều gì, ngay cả khi bạn thực sự, thực sự cần và rất dễ quên để làm điều đó. Vì vậy, bạn kết thúc với các lỗi khó chịu trong đó hai cấu trúc dữ liệu có tham chiếu đến cùng một lớp giá trị (ví dụ: Ngày) và sau đó một trong số chúng thay đổi Ngày và cấu trúc dữ liệu khác hiện bị hỏng.
kevin cline

3
Meh. Đó không phải là vấn đề - bất kỳ ai biết C ++ sẽ không phạm phải sai lầm này. Quan trọng hơn, không có lý do nào để thao túng các loại đa hình theo giá trị nếu bạn biết bạn đang làm gì. Bạn đang bắt đầu phản ứng giật toàn bộ đầu gối về khả năng mỏng về mặt kỹ thuật. Con trỏ NULL là một vấn đề lớn hơn.
DeadMG

8

Tất nhiên, bạn có thể làm cho nó được bảo vệ và trống, để các lớp dẫn xuất có thể chọn. Tuy nhiên, nói chung, mã của bạn bị cấm bằng mọi cách vì không thể khởi tạo IAbstract- bởi vì nó có chức năng ảo thuần túy. Như vậy, đây thường không phải là vấn đề - các lớp giao diện của bạn không thể được khởi tạo và do đó không bao giờ có thể được sao chép và các lớp dẫn xuất hơn của bạn có thể cấm hoặc tiếp tục sao chép theo ý muốn.


Và nếu có một hệ thống phân cấp Tóm tắt -> Derured1 -> Derured2 và Derured2 được gán cho Derured1 thì sao? Những góc tối của ngôn ngữ vẫn làm tôi ngạc nhiên.
Coder

@Coder: Điều đó thường được xem là PEBKAC. Tuy nhiên, trực tiếp hơn, bạn luôn có thể khai báo một tư nhân operator=(const Derived2&)trong Derived1.
DeadMG

Ok, có lẽ đây thực sự không phải là một vấn đề. Tôi chỉ muốn đảm bảo rằng lớp học được an toàn chống lại lạm dụng.
Coder

2
Và cho rằng bạn nên có được một huy chương.
Kỹ sư thế giới

2
@Coder: Lạm dụng bởi ai? Điều tốt nhất bạn có thể làm là làm cho nó dễ dàng để viết mã chính xác. Thật vô nghĩa khi cố gắng chống lại các lập trình viên không biết C ++.
kevin cline

2

Bằng cách đặt ctor và gán riêng tư (hoặc bằng cách khai báo chúng là = xóa trong C ++ 11), bạn vô hiệu hóa bản sao.

Vấn đề ở đây là bạn phải làm điều đó ở đâu. Để giữ nguyên mã của bạn, I Ab. Không phải là vấn đề. (lưu ý rằng làm những gì bạn đã làm, bạn gán tiểu dự án *a1 IAbstractcho a2, mất bất kỳ tham chiếu nào Derived. Việc gán giá trị không phải là đa hình)

Vấn đề đi kèm Derived::theproblem. Việc sao chép một Derogen vào một cái khác trong thực tế có thể chia sẻ *theproblemdữ liệu có thể không được thiết kế để chia sẻ (có hai trường hợp có thể gọi delete theproblemtrong hàm hủy của chúng).

Nếu đó là trường hợp, đó là Derivedphải không thể sao chép và không thể chuyển nhượng. Tất nhiên, nếu bạn đặt riêng tư bản sao vào IAbstract, vì bản sao mặc định cho Derivednhu cầu của nó, Derivedcũng sẽ không thể sao chép . Nhưng nếu bạn xác định bản thân Derived::Derived(const Derived&)mà không gọi IAbtractbản sao, bạn vẫn có thể sao chép chúng.

Vấn đề nằm ở Derogen và giải pháp phải nằm trong Derogen: nếu nó phải là một đối tượng chỉ động chỉ được truy cập bởi các con trỏ hoặc tham chiếu, thì chính Der Der phải có

class Derived
{
    ...
    Derived(const Derived&) = delete;
    Derived& operator=(const Derived&) = delete;
};

Về cơ bản, tùy thuộc vào người thiết kế lớp Derogen (cần biết cách thức hoạt động của Derogen và cách theproblemquản lý) để quyết định làm gì với sự phân công và sao chép.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.