Sự khác nhau giữa là gì
public
,private
vàprotected
thừa kế trong C ++?
Tất cả các câu hỏi tôi đã tìm thấy về SO giải quyết các trường hợp cụ thể.
Sự khác nhau giữa là gì
public
,private
vàprotected
thừa kế trong C ++?
Tất cả các câu hỏi tôi đã tìm thấy về SO giải quyết các trường hợp cụ thể.
Câu trả lời:
Để trả lời câu hỏi đó, trước tiên tôi muốn mô tả người truy cập của thành viên bằng từ ngữ của riêng tôi. Nếu bạn đã biết điều này, hãy bỏ qua tiêu đề "tiếp theo:".
Có ba accessors rằng tôi nhận thức: public
, protected
và private
.
Để cho:
class Base {
public:
int publicMember;
protected:
int protectedMember;
private:
int privateMember;
};
Base
được cũng nhận thức được Base
có chứa publicMember
.Base
có chứa protectedMember
.Base
nhận thức được privateMember
.Bởi "nhận thức được", ý tôi là "thừa nhận sự tồn tại của, và do đó có thể truy cập".
Điều tương tự cũng xảy ra với thừa kế công cộng, tư nhân và được bảo vệ. Hãy xem xét một lớp Base
và một lớp Child
kế thừa từ Base
.
public
, tất cả mọi thứ nhận thức được Base
và Child
cũng nhận thức được rằng Child
thừa kế từ Base
.protected
, chỉ Child
và con của nó, nhận thức được rằng chúng được thừa kế từ Base
.private
, không ai khác ngoài Child
nhận thức về thừa kế.SomeBase
đó giống như một cách mã hóa cứng để soạn - trong một thành viên ẩn danh thuộc loại SomeBase
. Điều này, giống như bất kỳ thành viên nào khác, có một công cụ xác định truy cập, thực hiện kiểm soát tương tự đối với truy cập bên ngoài.
class A
{
public:
int x;
protected:
int y;
private:
int z;
};
class B : public A
{
// x is public
// y is protected
// z is not accessible from B
};
class C : protected A
{
// x is protected
// y is protected
// z is not accessible from C
};
class D : private A // 'private' is default for classes
{
// x is private
// y is private
// z is not accessible from D
};
LƯU Ý QUAN TRỌNG: Các lớp B, C và D đều chứa các biến x, y và z. Nó chỉ là câu hỏi truy cập.
Về việc sử dụng quyền thừa kế được bảo vệ và riêng tư, bạn có thể đọc ở đây .
Hạn chế khả năng hiển thị của thừa kế sẽ làm cho mã không thể thấy rằng một số lớp kế thừa một lớp khác: Chuyển đổi ngầm định từ dẫn xuất sang cơ sở sẽ không hoạt động và static_cast
từ cơ sở sang dẫn xuất sẽ không hoạt động.
Chỉ thành viên / bạn bè của một lớp có thể thấy thừa kế riêng tư và chỉ thành viên / bạn bè và các lớp dẫn xuất mới có thể thấy thừa kế được bảo vệ.
thừa kế công cộng
IS-A thừa kế. Một nút là một cửa sổ, và bất cứ nơi nào cần một cửa sổ, một nút cũng có thể được thông qua.
class button : public window { };
thừa kế được bảo vệ
Được bảo vệ thực hiện theo các điều khoản. Hiếm khi hữu ích. Được sử dụng boost::compressed_pair
để lấy từ các lớp trống và lưu bộ nhớ bằng cách sử dụng tối ưu hóa lớp cơ sở trống (ví dụ bên dưới không sử dụng khuôn mẫu để tiếp tục duy trì điểm chính):
struct empty_pair_impl : protected empty_class_1
{ non_empty_class_2 second; };
struct pair : private empty_pair_impl {
non_empty_class_2 &second() {
return this->second;
}
empty_class_1 &first() {
return *this; // notice we return *this!
}
};
thừa kế tư nhân
Thực hiện trong điều khoản của. Việc sử dụng lớp cơ sở chỉ để thực hiện lớp dẫn xuất. Hữu ích với các đặc điểm và nếu kích thước có vấn đề (các đặc điểm trống chỉ chứa các hàm sẽ sử dụng tối ưu hóa lớp cơ sở trống). Thường thì ngăn chặn là giải pháp tốt hơn. Kích thước cho chuỗi là rất quan trọng, vì vậy đây là cách sử dụng thường thấy ở đây
template<typename StorageModel>
struct string : private StorageModel {
public:
void realloc() {
// uses inherited function
StorageModel::realloc();
}
};
thành viên công cộng
Tổng hợp
class pair {
public:
First first;
Second second;
};
Phụ kiện
class window {
public:
int getWidth() const;
};
thành viên được bảo vệ
Cung cấp quyền truy cập nâng cao cho các lớp dẫn xuất
class stack {
protected:
vector<element> c;
};
class window {
protected:
void registerClass(window_descriptor w);
};
thành viên tư nhân
Giữ chi tiết thực hiện
class window {
private:
int width;
};
Lưu ý rằng phôi kiểu C có chủ ý cho phép truyền một lớp dẫn xuất sang lớp cơ sở được bảo vệ hoặc riêng tư theo cách được xác định và an toàn và cũng chuyển sang hướng khác. Điều này nên tránh bằng mọi giá, vì nó có thể khiến mã phụ thuộc vào chi tiết triển khai - nhưng nếu cần, bạn có thể sử dụng kỹ thuật này.
Ba từ khóa này cũng được sử dụng trong một ngữ cảnh hoàn toàn khác để chỉ định mô hình kế thừa khả năng hiển thị .
Bảng này tập hợp tất cả các kết hợp có thể có của mô hình kế thừa và khai báo thành phần thể hiện quyền truy cập kết quả vào các thành phần khi lớp con được xác định hoàn toàn.
Bảng trên được diễn giải theo cách sau (hãy xem hàng đầu tiên):
nếu một thành phần được khai báo là công khai và lớp của nó được kế thừa là công khai thì kết quả truy cập là công khai .
Một ví dụ:
class Super {
public: int p;
private: int q;
protected: int r;
};
class Sub : private Super {};
class Subsub : public Sub {};
Kết quả là truy cập cho các biến p
, q
, r
trong lớp Subsub là none .
Một vi dụ khac:
class Super {
private: int x;
protected: int y;
public: int z;
};
class Sub : protected Super {};
Quyền truy cập kết quả cho các biến y
, z
trong lớp Sub được bảo vệ và đối với biến x
là không có .
Một ví dụ chi tiết hơn:
class Super {
private:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
int main(void) {
Super object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
Bây giờ hãy xác định một lớp con:
class Sub : Super { };
int main(void) {
Sub object;
object.put(100);
object.put(object.get());
cout << object.get() << endl;
return 0;
}
Lớp được định nghĩa có tên Sub là lớp con của lớp có tên Super
hoặc Sub
lớp đó được lấy từ Super
lớp. Các Sub
giới thiệu lớp không biến mới hay các chức năng mới. Điều đó có nghĩa là bất kỳ đối tượng nào của Sub
lớp đều thừa hưởng tất cả các đặc điểm sau khi Super
lớp thực sự là một bản sao của Super
các đối tượng của lớp?
Không . Nó không.
Nếu chúng tôi biên dịch mã sau đây, chúng tôi sẽ không nhận được gì ngoài các lỗi biên dịch nói rằng put
và get
các phương thức không thể truy cập được. Tại sao?
Khi chúng ta bỏ qua trình xác định khả năng hiển thị, trình biên dịch giả định rằng chúng ta sẽ áp dụng cái gọi là thừa kế riêng . Điều đó có nghĩa rằng tất cả các cộng đồng thành phần lớp cha biến thành tin truy cập, các thành phần lớp cha tư nhân sẽ không thể truy cập vào tất cả. Do đó, điều đó có nghĩa là bạn không được phép sử dụng cái sau trong lớp con.
Chúng tôi phải thông báo cho trình biên dịch rằng chúng tôi muốn duy trì chính sách truy cập được sử dụng trước đó.
class Sub : public Super { };
Đừng nhầm lẫn : điều đó không có nghĩa là các thành phần riêng tư của Super class (như biến lưu trữ) sẽ biến thành công khai theo cách hơi kỳ diệu. Các thành phần riêng tư sẽ vẫn riêng tư , công khai sẽ vẫn công khai .
Các đối tượng của Sub
lớp có thể làm "gần như" những điều tương tự như anh chị lớn của họ được tạo ra từ Super
lớp. "Hầu như" bởi vì thực tế là một lớp con cũng có nghĩa là lớp bị mất quyền truy cập vào các thành phần riêng của siêu lớp . Chúng ta không thể viết một hàm thành viên của Sub
lớp để có thể thao tác trực tiếp với biến lưu trữ.
Đây là một hạn chế rất nghiêm trọng. Có bất kỳ công việc xung quanh?
Có .
Cấp độ truy cập thứ ba được gọi là bảo vệ . Từ khóa được bảo vệ có nghĩa là thành phần được đánh dấu với nó hoạt động như một công khai khi được sử dụng bởi bất kỳ lớp con nào và trông giống như một thành phần riêng tư với phần còn lại của thế giới . - Điều này chỉ đúng với các lớp được kế thừa công khai (như lớp Siêu trong ví dụ của chúng tôi) -
class Super {
protected:
int storage;
public:
void put(int val) { storage = val; }
int get(void) { return storage; }
};
class Sub : public Super {
public:
void print(void) {cout << "storage = " << storage;}
};
int main(void) {
Sub object;
object.put(100);
object.put(object.get() + 1);
object.print();
return 0;
}
Như bạn thấy trong mã ví dụ, chúng ta có một chức năng mới cho Sub
lớp và nó thực hiện một điều quan trọng: nó truy cập biến lưu trữ từ lớp Super .
Sẽ không thể nếu biến được khai báo là riêng tư. Trong phạm vi hàm chính, biến vẫn được ẩn đi vì vậy nếu bạn viết bất cứ thứ gì như:
object.storage = 0;
Trình biên dịch sẽ thông báo cho bạn rằng nó là một error: 'int Super::storage' is protected
.
Cuối cùng, chương trình cuối cùng sẽ tạo ra đầu ra sau:
storage = 101
Nó có liên quan đến cách các thành viên công khai của lớp cơ sở được tiếp xúc từ lớp dẫn xuất.
Như litb chỉ ra, thừa kế công khai là thừa kế truyền thống mà bạn sẽ thấy trong hầu hết các ngôn ngữ lập trình. Đó là mô hình mối quan hệ "IS-A". Kế thừa tư nhân, một cái gì đó đặc biệt của AFAIK đối với C ++, là mối quan hệ "THỰC HIỆN TRONG ĐIỀU KHOẢN". Đó là bạn muốn sử dụng giao diện chung trong lớp dẫn xuất, nhưng không muốn người dùng của lớp dẫn xuất có quyền truy cập vào giao diện đó. Nhiều ý kiến cho rằng trong trường hợp này bạn nên tổng hợp lớp cơ sở, thay vì có lớp cơ sở làm cơ sở riêng, hãy tạo thành viên có nguồn gốc để sử dụng lại chức năng của lớp cơ sở.
Member in base class : Private Protected Public
Kiểu kế thừa : Đối tượng được kế thừa như :
Private : Inaccessible Private Private
Protected : Inaccessible Protected Protected
Public : Inaccessible Protected Public
1) Kế thừa công cộng :
a. Các thành viên riêng của lớp Base không thể truy cập được trong lớp Derogen.
b. Các thành viên được bảo vệ của lớp Base vẫn được bảo vệ trong lớp Derogen.
c. Các thành viên công khai của lớp Base vẫn công khai trong lớp Derogen.
Vì vậy, các lớp khác có thể sử dụng các thành viên công khai của lớp Cơ sở thông qua đối tượng lớp Derogen.
2) Bảo vệ quyền thừa kế :
a. Các thành viên riêng của lớp Base không thể truy cập được trong lớp Derogen.
b. Các thành viên được bảo vệ của lớp Base vẫn được bảo vệ trong lớp Derogen.
c. Các thành viên công khai của lớp Base cũng trở thành thành viên được bảo vệ của lớp Derogen.
Vì vậy, các lớp khác không thể sử dụng các thành viên công khai của lớp Cơ sở thông qua đối tượng lớp Derogen; nhưng chúng có sẵn cho lớp con của Derogen.
3) Kế thừa tư nhân :
a. Các thành viên riêng của lớp Base không thể truy cập được trong lớp Derogen.
b. Các thành viên được bảo vệ và công khai của lớp Cơ sở trở thành thành viên riêng của lớp Deriving.
Vì vậy, không có thành viên nào của lớp Base có thể được truy cập bởi các lớp khác thông qua đối tượng lớp Derogen vì chúng là riêng tư trong lớp Derogen. Vì vậy, ngay cả lớp con của lớp Derive cũng không thể truy cập chúng.
Kế thừa công khai mô hình mối quan hệ IS-A. Với
class B {};
class D : public B {};
mỗi D
là một B
.
Kế thừa tư nhân mô hình mối quan hệ IS-THỰC HIỆN-SỬ DỤNG (hoặc bất cứ điều gì được gọi là). Với
class B {};
class D : private B {};
một D
là không một B
, nhưng mỗi D
sử dụng của nó B
trong việc thực hiện của nó. Kế thừa tư nhân luôn có thể được loại bỏ bằng cách sử dụng ngăn chặn thay thế:
class B {};
class D {
private:
B b_;
};
Điều này D
cũng có thể được thực hiện bằng cách sử dụng B
, trong trường hợp này bằng cách sử dụng nó b_
. Ngăn chặn là một khớp nối ít chặt chẽ hơn giữa các loại so với thừa kế, vì vậy nói chung nó nên được ưu tiên. Đôi khi sử dụng ngăn chặn thay vì thừa kế riêng tư không thuận tiện như thừa kế riêng tư. Thường thì đó là một cái cớ khập khiễng vì lười biếng.
Tôi không nghĩ có ai biết protected
mô hình thừa kế. Ít nhất tôi chưa thấy lời giải thích thuyết phục nào.
D
xuất phát từ tư nhân D
, nó có thể ghi đè các chức năng ảo của B
. (Nếu, ví dụ, B
là một giao diện quan sát, sau đó D
có thể thực hiện nó và vượt qua this
chức năng đòi hỏi auch một giao diện, mà không cần tất cả mọi người có thể sử dụng D
như một người quan sát.) Ngoài ra, D
có chọn lọc có thể làm cho các thành viên của B
sẵn trong giao diện của nó bằng cách thực hiện using B::member
. Cả hai đều bất tiện về mặt cú pháp để thực hiện khi B
là thành viên.
protected
thừa kế Tôi thấy hữu ích với một virtual
lớp cơ sở và protected
ctor:struct CommonStuff { CommonStuff(Stuff*) {/* assert !=0 */ } }; struct HandlerMixin1 : protected virtual CommonStuff { protected: HandlerMixin1() : CommonStuff(nullptr) {} /*...*/ }; struct Handler : HandlerMixin1, ... { Handler(Stuff& stuff) : CommonStuff(&stuff) {} };
Nếu bạn thừa kế công khai từ một lớp khác, mọi người đều biết bạn đang thừa kế và bạn có thể được sử dụng đa hình bởi bất kỳ ai thông qua một con trỏ lớp cơ sở.
Nếu bạn kế thừa một cách bảo vệ, chỉ có các lớp con bạn mới có thể sử dụng bạn một cách đa hình.
Nếu bạn thừa kế riêng tư, bạn sẽ có thể thực thi các phương thức lớp cha.
Về cơ bản tượng trưng cho kiến thức mà các lớp còn lại có về mối quan hệ của bạn với lớp cha mẹ của bạn
Các thành viên dữ liệu được bảo vệ có thể được truy cập bởi bất kỳ lớp nào kế thừa từ lớp của bạn. Thành viên dữ liệu riêng tư, tuy nhiên, không thể. Hãy nói rằng chúng ta có những điều sau đây:
class MyClass {
private:
int myPrivateMember; // lol
protected:
int myProtectedMember;
};
Từ trong phần mở rộng của bạn đến lớp này, tham chiếu this.myPrivateMember
sẽ không hoạt động. Tuy nhiên, this.myProtectedMember
sẽ. Giá trị vẫn được gói gọn, vì vậy nếu chúng ta có một khởi tạo của lớp này được gọi myObj
, thì nó myObj.myProtectedMember
sẽ không hoạt động, do đó, nó có chức năng tương tự như một thành viên dữ liệu riêng tư.
Accessors | Base Class | Derived Class | World
—————————————+————————————+———————————————+———————
public | y | y | y
—————————————+————————————+———————————————+———————
protected | y | y | n
—————————————+————————————+———————————————+———————
private | | |
or | y | n | n
no accessor | | |
y: accessible
n: not accessible
Dựa trên ví dụ này cho java ... Tôi nghĩ rằng một bảng nhỏ đáng giá ngàn lời nói :)
Tóm lược:
Khi kế thừa, bạn có thể (trong một số ngôn ngữ) thay đổi loại bảo vệ của thành viên dữ liệu theo hướng nhất định, ví dụ: từ được bảo vệ sang công khai.
Các thành viên riêng của một lớp cơ sở chỉ có thể được truy cập bởi các thành viên của lớp cơ sở đó.
Các thành viên công khai của một lớp cơ sở có thể được truy cập bởi các thành viên của lớp cơ sở đó, các thành viên của lớp dẫn xuất của nó cũng như các thành viên nằm ngoài lớp cơ sở và lớp dẫn xuất.
Các thành viên được bảo vệ của một lớp cơ sở có thể được truy cập bởi các thành viên của lớp cơ sở cũng như các thành viên của lớp dẫn xuất của nó.
tư nhân : cơ sở
bảo vệ : cơ sở + dẫn xuất
công khai : cơ sở + dẫn xuất + bất kỳ thành viên nào khác
Tôi tìm thấy một câu trả lời dễ dàng và vì vậy tôi cũng nghĩ đến việc đăng nó để tham khảo trong tương lai.
Nó từ các liên kết http://www.learncpp.com/cpp-tutorial/115-inherribution-and-access-specifier/
class Base
{
public:
int m_nPublic; // can be accessed by anybody
private:
int m_nPrivate; // can only be accessed by Base member functions (but not derived classes)
protected:
int m_nProtected; // can be accessed by Base member functions, or derived classes.
};
class Derived: public Base
{
public:
Derived()
{
// Derived's access to Base members is not influenced by the type of inheritance used,
// so the following is always true:
m_nPublic = 1; // allowed: can access public base members from derived class
m_nPrivate = 2; // not allowed: can not access private base members from derived class
m_nProtected = 3; // allowed: can access protected base members from derived class
}
};
int main()
{
Base cBase;
cBase.m_nPublic = 1; // allowed: can access public members from outside class
cBase.m_nPrivate = 2; // not allowed: can not access private members from outside class
cBase.m_nProtected = 3; // not allowed: can not access protected members from outside class
}
Về cơ bản, nó là sự bảo vệ truy cập của công chúng và các thành viên được bảo vệ của lớp cơ sở trong lớp dẫn xuất. Với sự kế thừa công khai, lớp dẫn xuất có thể thấy các thành viên công khai và được bảo vệ của cơ sở. Với thừa kế tư nhân, nó không thể. Với sự bảo vệ, lớp dẫn xuất và bất kỳ lớp nào có nguồn gốc từ đó có thể nhìn thấy chúng.