Kế thừa: 'A' là một cơ sở không thể tiếp cận của 'B'


82
$ cat inheritance.cpp 
#include <iostream>

using namespace std;

class A { };
class B : private A { };

int main() {
    A* ab = new B;
}
$
$ g++ inheritance.cpp
inheritance.cpp: In function 'int main()':
inheritance.cpp:9: error: 'A' is an inaccessible base of 'B'
$

Tôi chỉ không hiểu lỗi này.

Theo tôi hiểu và như hướng dẫn này xác nhận, việc privatekế thừa chỉ nên thay đổi cách các thành viên của class Bhọ hiển thị với thế giới bên ngoài.

Tôi nghĩ rằng người chỉ định riêng tư đang làm nhiều hơn là chỉ thay đổi khả năng hiển thị của class Bcác thành viên ở đây.

  • Tôi gặp lỗi này là gì và nó có nghĩa là gì?
  • Về cơ bản, điều gì là sai khi cho phép loại mã này trong C ++? Trông hoàn toàn vô hại.

Câu trả lời:


100

Bằng cách đặt quyền thừa kế ở chế độ riêng tư, về cơ bản bạn đang nói rằng ngay cả thực tế là B thừa kế từ A (hoàn toàn) là riêng tư - không thể truy cập / hiển thị với thế giới bên ngoài.

Không cần phải thảo luận dài dòng về những gì sẽ xảy ra nếu nó được cho phép, sự thật đơn giản là nó không được phép. Nếu bạn muốn sử dụng một con trỏ để cơ sở để tham chiếu đến một đối tượng có kiểu dẫn xuất, thì bạn đang gặp khá nhiều khó khăn với việc sử dụng kế thừa công khai.

Thừa kế riêng không nhất thiết (hoặc thậm chí bình thường) nhằm tuân theo nguyên tắc thay thế Liskov . Thừa kế công khai khẳng định rằng một đối tượng dẫn xuất có thể được thay thế cho một đối tượng của lớp cơ sở và ngữ nghĩa phù hợp vẫn sẽ dẫn đến kết quả. Thừa kế riêng không khẳng định điều đó. Mô tả thông thường về mối quan hệ được ngụ ý bởi thừa kế riêng là "được thực hiện theo điều kiện của".

Kế thừa công khai có nghĩa là một lớp dẫn xuất duy trì tất cả các khả năng của lớp cơ sở và có khả năng bổ sung thêm bên cạnh đó. Kế thừa riêng thường ít nhiều có nghĩa ngược lại: rằng lớp dẫn xuất sử dụng một lớp cơ sở chung để triển khai thứ gì đó với giao diện hạn chế hơn.

Ví dụ, chúng ta hãy giả sử rằng các vùng chứa trong thư viện chuẩn C ++ được triển khai bằng cách sử dụng kế thừa chứ không phải mẫu. Trong hệ thống hiện tại, std::dequestd::vectorlà các vùng chứa và std::stacklà một bộ điều hợp vùng chứa cung cấp giao diện hạn chế hơn. Vì nó dựa trên các mẫu, bạn có thể sử dụng std::stacklàm bộ điều hợp cho một trong hai std::dequehoặc std::vector.

Nếu chúng tôi muốn cung cấp về cơ bản giống với kế thừa, chúng tôi có thể sẽ sử dụng kế thừa riêng, vì vậy std::stacksẽ giống như:

class stack : private vector {
    // ...
};

Trong trường hợp này, chúng tôi chắc chắn không muốn người dùng có thể thao túng chúng tôi stacknhư thể nó là một vector. Làm như vậy có thể (và có khả năng) sẽ vi phạm các kỳ vọng về ngăn xếp (ví dụ: người dùng có thể chèn / xóa các mục ở giữa, thay vì theo kiểu hoàn toàn giống ngăn xếp như dự định). Về cơ bản chúng tôi đang sử dụng vectornhư một cách thuận tiện để triển khai ngăn xếp của mình, nhưng nếu (ví dụ) chúng tôi đã thay đổi việc triển khai thành stackmột mình (không phụ thuộc vào một lớp cơ sở) hoặc triển khai lại nó về mặt std::deque, chúng tôi không muốn điều đó để ảnh hưởng đến bất kỳ mã máy khách nào - đối với mã máy khách, điều này được cho là chỉ là một ngăn xếp, không phải là một số vector (hoặc deque) chuyên biệt nào đó.


1
điều này cũng áp dụng choprotected
SubMachine

12

thừa kế riêng chỉ nên thay đổi cách các thành viên của lớp B hiển thị với thế giới bên ngoài

Nó có. Và nếu

A* p = new B;

được cho phép, sau đó các thành viên kế thừa của bất kỳ Bcó thể được truy cập từ thế giới bên ngoài, chỉ bằng cách thực hiện một A*. Vì chúng được kế thừa một cách riêng tư, nên quyền truy cập đó là bất hợp pháp và dự báo cũng vậy.


8

clang++ đưa ra một thông báo lỗi dễ hiểu hơn một chút:

example.cpp:9:13: error: cannot cast 'B' to its private base class 'A'
    A* ab = new B;
            ^
example.cpp:6:11: note: declared private here
class B : private A { };
          ^~~~~~~~~
1 error generated.

Tôi không phải là chuyên gia C ++, nhưng có vẻ như nó chỉ đơn giản là không được phép. Tôi sẽ đi thăm dò thông số kỹ thuật và xem những gì tôi nghĩ ra.

Chỉnh sửa: đây là tài liệu tham khảo có liên quan từ thông số kỹ thuật - Phần 4.10 Chuyển đổi con trỏ , đoạn 3:

Giá trị pr của kiểu "con trỏ tới cv D ", trong đó Dlà kiểu lớp, có thể được chuyển đổi thành giá trị p của kiểu "con trỏ tới cv B", trong đó B là lớp cơ sở của D. Nếu Blà một lớp cơ sở không thể truy cập được hoặc không rõ ràng của D, một chương trình yêu cầu chuyển đổi này là không hợp lệ.


5

Nó khá đơn giản: thực tế Ađược kế thừa một cách riêng tư có nghĩa là thực tế Bmở rộng Alà một bí mật và chỉ B"biết" nó. Đó là định nghĩa về thừa kế tư nhân.


4
Nhưng tôi nhận được cùng một lỗi nếu tôi thay thế privatebằng protected.
Lazer

2
Thật. "Được bảo vệ" có nghĩa là kiến ​​thức được giới hạn ở Bvà các lớp con (và bạn bè) của B. " A* ab = new B;" sẽ hợp pháp trong một lớp giả định Clà một lớp con của B.
Ernest Friedman-Hill

3

Kế thừa riêng có nghĩa là bên ngoài lớp dẫn xuất, thông tin kế thừa bị ẩn. Điều đó có nghĩa là bạn không thể truyền lớp dẫn xuất sang lớp cơ sở: người gọi không biết mối quan hệ.


Cảm ơn, nhưng bằng cách nào đó nó không có ý nghĩa. privateCông việc kinh doanh duy nhất nên là kiểm soát cách các thành viên cư xử. Sẽ có tác hại gì nếu thông tin thừa kế không bị che giấu?
Lazer

1
Thừa kế riêng là một dạng tổng hợp / cấu thành. Đó là một cách để các thuộc tính của một lớp cơ sở, mà không phải một đối tượng của lớp cơ sở. Nếu đó không phải là điều bạn muốn, thì tài sản thừa kế riêng không dành cho bạn. Đó chỉ là cách nó hoạt động.
tmpearce

0

Cái này đang hoạt động

#include <iostream>

using namespace std;

class A{
    public:
        virtual void update() = 0;
};

class B: public A{
    public:
    virtual void update(){std::cout<<"hello";};
};

int main()
{
    A *a = new B();

    a->update();

    return 0;
}
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.