2013-04-29 34 views
9

liên quan: Does "virtual base class in the case of multilevel inheritance" have significanceC++ tại sao thừa kế ảo cho phép phòng ngừa thừa kế tiếp theo?

Tôi có một lớp mẫu có thể được thừa hưởng từ để truyền đạt một số lựa chọn chức năng. Tuy nhiên, nó muốn ngăn chặn bất kỳ lớp học từ kế thừa thêm từ bất cứ điều gì thừa kế nó.

Sau đây dường như đạt được điều này:

template<typename Child> 
class SealingClass 
    { 
    public: 
    /*public methods etc*/ 
    private: 
    SealingClass() {} 
    friend Child; 
    }; 

//simplify a bit: 
#define Seal(x) public virtual SealingClass<x> 

Bây giờ, tôi có thể kế thừa từ các lớp trên, như sau:

class NewClass: Seal(NewClass) {}; 

Và nếu tôi sau đó cố gắng kế thừa một lần nữa từ NewClass, như trong :

class AnotherClass: public NewClass {}; 

và sau đó tạo một thể hiện của lớp đã nói:

AnotherClass a; 

Tôi gặp lỗi mong muốn, liên quan đến hàm tạo trong SealingClass là riêng tư.

Vì vậy, mọi thứ hoạt động như tôi muốn!

Tuy nhiên, tôi đã nhận thấy rằng nếu tôi loại bỏ các từ khóa virtual từ xác định ..

#define Seal(x) public SealingClass<x> 

..my instantiation của AnotherClass hiện đang làm việc tốt.

Tôi hiểu rằng từ khóa virtual, trong ngữ cảnh này, có nghĩa là chỉ một thể hiện của lớp cơ sở được xác định trong trường hợp đa thừa kế (ví dụ: thừa kế kim cương).

Nhưng, tại sao nó ảnh hưởng đến chức năng của các bên trên?

Cảm ơn :)

+0

Từ perusing câu trả lời được liên kết ở trên, có vẻ như đó là do thứ tự các lớp được thừa hưởng hầu như được kế thừa, điều này sẽ có ý nghĩa! – jsdw

+1

IIRC bạn phải khởi tạo một lớp cơ sở ảo trong kiểu dẫn xuất nhất, trong khi bạn khởi tạo các lớp cơ sở không ảo trong kiểu dẫn xuất tiếp theo. – dyp

+1

Tôi cho rằng bạn cũng có một hàm tạo bản sao 'private'? Nếu không, bạn có thể chỉ đơn giản là làm cho tất cả các nhà xây dựng con của bạn sử dụng để xây dựng 'SealingClass', bỏ qua hoàn toàn con dấu. –

Trả lời

7

Nếu sử dụng thừa kế ảo, loại có nguồn gốc nhiều nhất phải khởi tạo lớp cơ sở ảo này. Nếu bạn không sử dụng thừa kế ảo, loại có nguồn gốc trực tiếp phải thực hiện khởi tạo.

Do đó, ctor tin không ngăn cản các nguồn gốc loại NewClass từ khởi tạo các lớp cơ sở trực tiếp SealingClass, và AnotherClass không phải khởi tạo NewClass nếu nó chưa được hầu như thừa hưởng.


Một số ví dụ:

template<typename Child> 
class SealingClass { 
public: // for now 
    SealingClass() {} 
}; 

class NewClass : public SealingClass<T> { 
public: 
    NewClass() : SealingClass<T>() {} // allowed, SealingClass<T> is a 
             // direct base class 
}; 

class AnotherClass : public NewClass { 
public: 
    AnotherClass() : NewClass() {}  // allowed, NewClass is a 
              // direct base class 
    AnotherClass() : SealingClass<T>() {} // not allowed, SealingClass<T> is 
              // no direct nor a virtual base class 
}; 


class NewClass_v : public virtual SealingClass<T> { 
public: 
    NewClass_v() : SealingClass<T>() {} // allowed, SealingClass<T> is a 
              // direct base class 
}; 

class AnotherClass_v : public NewClass_v { 
public: 
    AnotherClass_v() : NewClass_v() {}  // allowed, NewClass_virt is a 
               // direct base class 
    AnotherClass_v() : SealingClass<T>() {} // allowed, SealingClass<T> is a 
               // virtual base class 
}; 

Bây giờ, nếu ctor của SealingClass là tư nhân, AnotherClass_virt không được phép gọi ctor này do specifier private truy cập và không phải là một người bạn.

Nếu bạn bỏ mặc định khởi tạo của lớp cơ sở (dù là ảo hay trực tiếp), nó được khởi tạo mặc định ([class.base.init]/8), đó là, ctor mặc định được gọi ngầm (nhưng bạn vẫn phải có quyền truy cập vào ctor, vì vậy nó giống như một cách rõ ràng writting cuộc gọi đến ctor mặc định).


Một số trích dẫn:

[class.base.init]/1

Trong định nghĩa của một nhà xây dựng cho một lớp học, initializers cho subobjects cơ sở trực tiếp và ảo và phi thành viên dữ liệu -static có thể được chỉ định bởi một ctor-initializer

[class.base.init]/7

Trình khởi tạo mem nơi mem-initializer-id biểu thị một lớp cơ sở ảo bị bỏ qua trong khi thực hiện hàm tạo bất kỳ lớp nào không phải lớp xuất phát nhất.

[class.base.init]/10

Trong một constructor không ủy thác, tiền thu được khởi tạo theo trình tự sau:

  • Đầu tiên, và chỉ dành cho các nhà xây dựng của lớp cơ bản nhất, các lớp cơ sở ảo được khởi tạo theo thứ tự chúng xuất hiện trên một chiều ngang trái-phải-phải của biểu đồ tuần hoàn của các lớp cơ sở, trong đó “từ trái sang phải” là thứ tự xuất hiện của các lớp cơ sở trong t ông bắt nguồn từ lớp cơ sở-specifier-danh sách.
  • Sau đó, các lớp cơ sở trực tiếp được khởi tạo theo thứ tự khai báo khi chúng xuất hiện trong danh sách-specifier-list (bất kể thứ tự của bộ khởi tạo mem).

Mỏ nhấn mạnh.

+0

Cảm ơn; báo giá cuối cùng 'Đầu tiên, và chỉ dành cho hàm tạo của lớp dẫn xuất nhất, các lớp cơ sở ảo được khởi tạo theo thứ tự chúng xuất hiện trên một chiều ngang trái-phải-phải của biểu đồ tuần hoàn của các lớp cơ sở ..' là đặc biệt hữu ích khi nhìn thấy những gì đang xảy ra ở đây! – jsdw