Thành viên riêng tư và được bảo vệ: C ++


276

Ai đó có thể khai sáng cho tôi về sự khác biệt giữa privateprotectedcác thành viên trong các lớp học không?

Tôi hiểu từ các quy ước thực hành tốt nhất rằng các biến và hàm không được gọi bên ngoài lớp nên được thực hiện private- nhưng nhìn vào dự án MFC của tôi , MFC dường như ủng hộ protected.

Sự khác biệt và tôi nên sử dụng là gì?

Câu trả lời:


374

Các thành viên tư nhân chỉ có thể truy cập trong lớp xác định chúng.

Các thành viên được bảo vệ có thể truy cập trong lớp xác định chúng và trong các lớp kế thừa từ lớp đó.

Chỉnh sửa: Cả hai cũng có thể được truy cập bởi bạn bè của lớp của họ và trong trường hợp thành viên được bảo vệ, bởi bạn bè của các lớp dẫn xuất của họ.

Chỉnh sửa 2: Sử dụng bất cứ điều gì có ý nghĩa trong bối cảnh vấn đề của bạn. Bạn nên cố gắng làm cho các thành viên riêng tư bất cứ khi nào bạn có thể để giảm khớp nối và bảo vệ việc thực hiện lớp cơ sở, nhưng nếu điều đó là không thể thì hãy sử dụng các thành viên được bảo vệ. Kiểm tra C ++ FAQ để hiểu rõ hơn về vấn đề này. Câu hỏi này về các biến được bảo vệ cũng có thể giúp đỡ.


12
Liên kết đến C ++ FAQ Lite đã chuyển sang isocpp.org/wiki/faq/basics-of-inherribution
avner 14/08/2015

134

Các thành viên công cộng của lớp A có thể truy cập được cho tất cả mọi người và mọi người.

Các thành viên được bảo vệ của lớp A không thể truy cập được bên ngoài mã của A, nhưng có thể truy cập được từ mã của bất kỳ lớp nào có nguồn gốc từ A.

Các thành viên riêng của lớp A không thể truy cập được bên ngoài mã của A hoặc từ mã của bất kỳ lớp nào có nguồn gốc từ A.

Vì vậy, cuối cùng, lựa chọn giữa bảo vệ hoặc riêng tư là trả lời các câu hỏi sau: Bạn sẵn sàng đặt bao nhiêu niềm tin vào lập trình viên của lớp dẫn xuất?

Theo mặc định , giả sử lớp dẫn xuất không đáng tin cậy và làm cho các thành viên của bạn riêng tư . Nếu bạn có một lý do rất chính đáng để cấp quyền truy cập miễn phí nội bộ của lớp mẹ vào các lớp dẫn xuất của nó, thì bạn có thể làm cho chúng được bảo vệ.


Lớp dẫn xuất phải là một loại lớp của bạn và dữ liệu được bảo vệ của lớp cơ sở là một phần của dữ liệu của lớp dẫn xuất. Người viết của lớp dẫn xuất dự kiến ​​sẽ xử lý dữ liệu này đúng cách hoặc đó là một lỗi. Tuy nhiên, dữ liệu riêng tư trong một lớp cơ sở là thứ mà người viết của lớp dẫn xuất không kiểm soát.
CashCow

@CashCow the protected data of the base class is part of the data of the derived class.Thật vậy. Sau đó, tốt hơn là để người viết của lớp dẫn xuất khai báo dữ liệu đó trong lớp của họ, thay vì của tôi? ... :-) ... The writer of the derived class is expected to handle this data properly or it is a bug.Trong mẫu NVI, mục đích là để mọi thứ riêng tư, bao gồm các phương thức, để hạn chế thiệt hại mà người viết lớp dẫn xuất có thể làm đối với hệ thống phân cấp. Phương pháp bảo vệ đã là một vấn đề tiềm năng. Tôi không bị thuyết phục làm nặng thêm điều này bằng cách sử dụng trạng thái được bảo vệ là cách tiếp cận đúng đắn.
paercebal

Nó có thể, sẽ yêu cầu bạn phải có "getters" ảo trong lớp cơ sở để truy cập nó. Và trong khi bạn có thể có các lớp ở giữa để thực hiện các cách khác nhau thì mẫu dữ liệu có thể được thực hiện, không phải lúc nào cũng thực tế để làm như vậy. Ví dụ: "mẫu", phổ biến trong các ngôn ngữ không có công cụ sửa đổi "const" mặc dù không cần thiết hầu hết thời gian trong C ++ là có một lớp cơ sở chỉ đọc và các lớp dẫn xuất có thể ghi. Trong C ++, điều này cũng có thể tốt chỉ đơn giản vì bạn muốn có nhiều hơn một cách có thể để tải (khởi tạo) dữ liệu.
CashCow

Có nhiều cách khác nhau để làm điều đó. Làm cho các lớp serialization của bạn bè. Đặt tất cả dữ liệu của bạn vào một cấu trúc có quyền truy cập công khai nhưng lớp của bạn có một thành viên riêng của biến này .... Các thành viên được bảo vệ và các lớp dẫn xuất để tải nó từ bất cứ nguồn nào đôi khi dễ dàng hơn.
CashCow

63

Các thành viên được bảo vệ có thể được truy cập từ các lớp dẫn xuất. Những người riêng tư không thể.

class Base {

private: 
  int MyPrivateInt;
protected: 
  int MyProtectedInt;
public:
  int MyPublicInt;
};

class Derived : Base
{
public:
  int foo1()  { return MyPrivateInt;} // Won't compile!
  int foo2()  { return MyProtectedInt;} // OK  
  int foo3()  { return MyPublicInt;} // OK
};‌‌

class Unrelated 
{
private:
  Base B;
public:
  int foo1()  { return B.MyPrivateInt;} // Won't compile!
  int foo2()  { return B.MyProtectedInt;} // Won't compile
  int foo3()  { return B.MyPublicInt;} // OK
};

Về mặt "thực hành tốt nhất", nó phụ thuộc. Nếu thậm chí có khả năng mờ nhạt rằng ai đó có thể muốn lấy một lớp mới từ lớp hiện tại của bạn và cần truy cập vào các thành viên nội bộ, hãy làm cho họ được Bảo vệ, chứ không phải Riêng tư. Nếu chúng là riêng tư, lớp của bạn có thể trở nên khó kế thừa từ một cách dễ dàng.


3
Tôi xin khác: nếu có một khả năng mờ nhạt rằng sẽ không có lớp con nào cần nó, hãy đặt nó ở chế độ riêng tư. Trừ khi bạn có ý định để lớp của bạn được phân lớp, hãy sử dụng mẫu phương thức mẫu.
xtofl

23

Lý do mà MFC ủng hộ bảo vệ, là bởi vì nó là một khung. Bạn có thể muốn phân lớp các lớp MFC và trong trường hợp đó cần có giao diện được bảo vệ để truy cập các phương thức không thể nhìn thấy đối với việc sử dụng chung của lớp.


9

Tất cả phụ thuộc vào những gì bạn muốn làm và những gì bạn muốn các lớp dẫn xuất có thể nhìn thấy.

class A
{
private:
    int _privInt = 0;
    int privFunc(){return 0;}
    virtual int privVirtFunc(){return 0;}
protected:
    int _protInt = 0;
    int protFunc(){return 0;}
public:
    int _publInt = 0;
    int publFunc()
    {
         return privVirtFunc();
    }
};

class B : public A
{
private:
    virtual int privVirtFunc(){return 1;}
public:
    void func()
    {
        _privInt = 1; // wont work
        _protInt = 1; // will work
        _publInt = 1; // will work
        privFunc(); // wont work
        privVirtFunc(); // wont work
        protFunc(); // will work
        publFunc(); // will return 1 since it's overridden in this class
    }
}

6

Các thuộc tính và phương thức được đánh dấu protectedlà - không giống như các thuộc tính riêng tư - vẫn hiển thị trong các lớp con.

Trừ khi bạn không muốn sử dụng hoặc cung cấp khả năng ghi đè phương thức trong các lớp con có thể, tôi sẽ tạo chúng private.


2
Một lớp dẫn xuất có thể ghi đè các hàm ảo riêng của cơ sở
James Hopkin

6

Chắc chắn hãy xem câu hỏi Biến thành viên được bảo vệ . Bạn nên sử dụng private làm mặc định (giống như C ++ classses do) để giảm khớp nối. Các biến thành viên được bảo vệ hầu hết luôn là một ý tưởng tồi, các hàm thành viên được bảo vệ có thể được sử dụng, ví dụ như mẫu Phương thức mẫu.


Thật buồn cười, tôi đã chỉnh sửa nó thành bài viết của mình trước khi tôi thấy bài viết của bạn. Được khuyến khích vì những con chim lông vũ vấp phải cùng một liên kết :)
Firas Assaad

4

Các thành viên được bảo vệ chỉ có thể được truy cập bởi con cháu của lớp và bằng mã trong cùng một mô-đun. Các thành viên tư nhân chỉ có thể được truy cập bởi lớp mà họ khai báo và bằng mã trong cùng một mô-đun.

Tất nhiên chức năng bạn bè ném cái này ra ngoài cửa sổ, nhưng ồ.


4

Các thành viên riêng chỉ có thể truy cập từ trong lớp, các thành viên được bảo vệ có thể truy cập được trong lớp và các lớp dẫn xuất. Đó là một tính năng của sự kế thừa trong các ngôn ngữ OO.

Bạn có thể có quyền thừa kế riêng tư, được bảo vệ và công khai trong C ++, điều này sẽ xác định những lớp dẫn xuất nào có thể truy cập trong hệ thống phân cấp thừa kế. C # ví dụ chỉ có thừa kế công khai.


3

private = chỉ có thể truy cập bởi quyền làm mẹ (lớp cơ sở) (tức là chỉ cha mẹ tôi mới có thể vào phòng ngủ của bố mẹ tôi)

bảo vệ = có thể truy cập bằng cách làm mẹ (lớp cơ sở) và con gái của cô ấy (tức là chỉ có bố mẹ tôi mới có thể vào phòng ngủ của bố mẹ tôi, nhưng đã cho phép con trai / con gái bước vào phòng ngủ của bố mẹ)

công khai = có thể truy cập bằng cách làm mẹ (lớp cơ sở), con gái và mọi người khác (nghĩa là chỉ có bố mẹ tôi mới có thể vào phòng ngủ của bố mẹ tôi, nhưng đó là bữa tiệc tại nhà - mi casa su casa)


2

Vì không có chức năng thành viên nào là cần thiết để tìm nạp và cập nhật các thành viên được bảo vệ trong lớp dẫn xuất, điều này làm tăng hiệu quả của mã và giảm số lượng mã chúng ta cần viết. Tuy nhiên, lập trình viên của lớp dẫn xuất được cho là nhận thức được những gì anh ta đang làm.


Bạn luôn có thể sử dụng một hàm nội tuyến được thực hiện trong khai báo lớp. Trình biên dịch sẽ tối ưu hóa điều đó (và đó sẽ là một cách tốt để thực thi quyền truy cập chỉ đọc vào một biến thành viên riêng chẳng hạn).
Paul Sanders

2

privateđược ưa thích cho dữ liệu thành viên. Các thành viên trong các lớp C ++ privatetheo mặc định.

publicđược ưa thích cho các chức năng thành viên, mặc dù đó là một vấn đề quan điểm. Ít nhất một số phương pháp phải có thể truy cập được. publiccó thể truy cập được cho tất cả. Đây là tùy chọn linh hoạt nhất và ít an toàn nhất. Bất cứ ai cũng có thể sử dụng chúng, và bất cứ ai cũng có thể sử dụng sai chúng.

privatekhông thể truy cập được Không ai có thể sử dụng chúng bên ngoài lớp học, và không ai có thể lạm dụng chúng. Ngay cả trong các lớp dẫn xuất.

protectedlà một sự thỏa hiệp bởi vì nó có thể được sử dụng trong các lớp dẫn xuất. Khi bạn xuất phát từ một lớp, bạn có hiểu biết tốt về lớp cơ sở và bạn cẩn thận không lạm dụng các thành viên này.

MFC là một trình bao bọc C ++ cho API Windows, nó thích publicprotected. Lớp học được tạo ra bởi Visual Studio WIZARD có một sự pha trộn xấu xí của protected, publicprivatecác thành viên. Nhưng có một số logic cho các lớp MFC.

Các thành viên như vậy SetWindowTextpublicvì bạn thường cần truy cập vào các thành viên này.

Các thành viên như OnLButtonDown, xử lý các thông báo nhận được bởi cửa sổ. Họ không nên được truy cập, do đó họ là protected. Bạn vẫn có thể truy cập chúng trong lớp dẫn xuất để ghi đè các hàm này.

Một số thành viên phải thực hiện các luồng và vòng lặp thông báo, chúng không nên được truy cập hoặc ghi đè, vì vậy chúng được khai báo là private

Trong cấu trúc C ++, các thành viên publictheo mặc định. Các cấu trúc thường chỉ được sử dụng cho dữ liệu, không phải phương thức, do đó publickhai báo được coi là an toàn.


1
Bạn viết "Thành viên trong các lớp C ++ được bảo vệ theo mặc định". Theo tiêu chuẩn, chúng là riêng tư hoặc công khai theo mặc định, tùy thuộc vào từ khóa nào được sử dụng trong định nghĩa (14p3). Microsoft có đi chệch khỏi tiêu chuẩn ở đây không?
Alexander Klauer

@AlexanderKlauer Tôi đã sai, privatetheo mặc định trong Visual Studio. Nó cũng privatetheo mặc định trong gcc, nó không bao giờ publicmặc định. Trừ khi tôi lại sai. Tôi không thể tìm thấy tiêu chuẩn mà bạn đang đề cập đến.
Barmak Shemirani

Xin lỗi, tôi nên đã được cụ thể hơn. Tôi đã tham khảo tiêu chuẩn C ++ 17. Chuẩn C ++ 11 có cùng từ ngữ trong 11p3. Bạn có thể cập nhật câu trả lời của bạn? Cảm ơn!
Alexander Klauer

1

Thành viên tư nhân chỉ có thể được truy cập trong cùng một lớp nơi nó đã khai báo nơi mà thành viên được bảo vệ có thể được truy cập trong lớp nơi nó được khai báo cùng với các lớp được kế thừa bởi nó.


1
  • Riêng tư : Đây là một công cụ xác định truy cập. Theo mặc định, các biến thể hiện (thành viên) hoặc các phương thức của một lớp trong c ++ / java là riêng tư. Trong quá trình kế thừa, mã và dữ liệu luôn được kế thừa nhưng không thể truy cập bên ngoài lớp. Chúng tôi có thể tuyên bố các thành viên dữ liệu của mình là riêng tư để không ai có thể thay đổi trực tiếp các biến thành viên của chúng tôi và chúng tôi có thể cung cấp các biểu đồ và setters công khai để thay đổi các thành viên riêng tư của chúng tôi. Và khái niệm này luôn được áp dụng trong quy tắc kinh doanh.

  • Được bảo vệ : Nó cũng là một specifier truy cập. Trong C ++, các thành viên được bảo vệ có thể truy cập trong lớp và lớp kế thừa nhưng không nằm ngoài lớp. Trong Java, các thành viên được bảo vệ có thể truy cập được trong lớp, đến lớp được kế thừa cũng như tất cả các lớp trong cùng một gói.


0

Một thành viên của lớp cơ sở không được bảo vệ có thể được truy cập bởi các thành viên và bạn bè của bất kỳ lớp nào có nguồn gốc từ lớp cơ sở đó bằng cách sử dụng một trong các cách sau:

  • Một con trỏ đến một lớp dẫn xuất trực tiếp hoặc gián tiếp
  • Một tham chiếu đến một lớp dẫn xuất trực tiếp hoặc gián tiếp
  • Một đối tượng của một lớp dẫn xuất trực tiếp hoặc gián tiếp

0

Riêng tư: Có thể truy cập bởi các chức năng thành viên lớp & chức năng bạn bè hoặc lớp bạn bè. Đối với lớp C ++, đây là công cụ xác định truy cập mặc định.

Được bảo vệ: Có thể truy cập bởi các chức năng thành viên lớp, chức năng bạn bè hoặc lớp bạn bè & các lớp dẫn xuất.

  • Bạn có thể giữ biến hoặc hàm thành viên lớp (thậm chí typedefs hoặc lớp bên trong) ở chế độ riêng tư hoặc được bảo vệ theo yêu cầu của bạn.
  • Hầu hết thời gian bạn giữ thành viên lớp là riêng tư và thêm các hàm get / set để đóng gói. Điều này giúp bảo trì mã.
  • Nói chung, chức năng riêng tư được sử dụng khi bạn muốn giữ các chức năng công khai của mình ở dạng mô-đun hoặc loại bỏ mã lặp lại thay vì ghi toàn bộ mã vào một chức năng. Điều này giúp bảo trì mã.

Tham khảo liên kết này để biết thêm chi tiết.


-2

Các bộ sửa đổi truy cập riêng tư và được bảo vệ là một và giống nhau mà các thành viên được bảo vệ của lớp cơ sở có thể được truy cập bên ngoài phạm vi của lớp cơ sở trong lớp con (dẫn xuất). Nó cũng áp dụng tương tự cho thừa kế. Nhưng với công cụ sửa đổi riêng, các thành viên của lớp cơ sở chỉ có thể được truy cập trong phạm vi hoặc mã của lớp cơ sở và các chức năng bạn bè của nó chỉ '' ''


5
Câu trả lời của bạn có giá trị gì so với các câu trả lời khác?
Hermann Döppes
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.