2010-04-01 23 views
13

Tôi nhận được lỗi biên dịch mà tôi hơi nhầm lẫn. Đây là trên VS2003.Tại sao thành viên superclass được bảo vệ không thể được truy cập trong một hàm lớp con khi được chuyển làm đối số?

lỗi C2248: 'A :: y': không thể truy cập được bảo vệ thành viên khai báo trong lớp 'A'

class A 
{ 
public: 
    A() : x(0), y(0) {} 
protected: 
    int x; 
    int y; 
}; 

class B : public A 
{ 
public: 
    B() : A(), z(0) {} 
    B(const A& item) : A(), z(1) { x = item.y;} 
private: 
    int z; 
}; 

Vấn đề là với x = item.y;

Quyền truy cập được chỉ định là được bảo vệ. Tại sao constructor của lớp B không thể truy cập A :: y?

Trả lời

5

Đó là vì điều này:

class base_class 
{ 
protected: 
    virtual void foo() { std::cout << "base::foo()" << std::endl; } 
}; 

class A : public base_class 
{ 
protected: 
    virtual void foo() { std::cout << "A::foo()" << std::endl; } 
}; 

class B : public base_class 
{ 
protected: 
    virtual void foo() { std::cout << "B::foo()" << std::endl; } 

public: 
    void bar(base_class *b) { b->foo(); } 
}; 

Nếu đó là hợp pháp, bạn có thể làm điều này:

A a; 
B b; 
b.bar(&a); 

Và bạn muốn được gọi một thành viên protected của A từ B, isn' t được phép.

+0

có lẽ, bạn đã quên 'ảo'? –

+1

Pavel: oops, yeah, tôi đã thêm từ khóa 'virtual' ... nó không thực sự thay đổi quan điểm của tôi, mặc dù :) –

1

tài liệu của IBM tóm tắt nó tốt nhất:

Một bảo vệ lớp cơ sở không tĩnh thành viên 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 học có nguồn gốc từ rằng lớp cơ sở bằng cách sử dụng một trong những sau:

  • Con trỏ đến lớp được dẫn xuất trực tiếp hoặc gián tiếp
  • Tham chiếu đến 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 trực tiếp hoặc gián tiếp có nguồn gốc

Vì vậy, sử dụng ví dụ của bạn ở trên làm cơ sở:

B::B(const A& item) : A(), z(1) { 
    // NOT OK because `item` is not a reference to the derived class B 
    //int i = item.y; 

    // OK because `item` reinterpreted as a reference to the derived class B 
    // Do not do this (bad!) -- for illustrative purposes only 
    int i = reinterpret_cast< const B& >(item).y; 

    // OK because it is equivalent to `this->x = i`, 
    // where `this` is a pointer to the derived class B 
    x = i; 
} 
3

Những câu trả lời khác giải thích lý do đằng sau việc ngăn chặn đối tượng B của bạn từ truy cập các bộ phận được bảo vệ của A trong ví dụ của bạn, mặc dù B 'là-a' A. Tất nhiên, cách dễ nhất để khắc phục sự cố này là làm cho các phần của A you want access to công khai` hoặc có phương thức truy cập công khai có thể truy cập.

Tuy nhiên bạn có thể quyết định điều đó không phù hợp (hoặc bạn có thể không có quyền kiểm soát định nghĩa của A). Dưới đây là một số gợi ý để cho phép bạn giải quyết vấn đề, theo thứ tự tăng dần kiểm soát truy cập của điều khiển truy cập. Lưu ý rằng tất cả các cách giải quyết này giả định rằng class A có thể sao chép được.

Trong trường hợp đầu tiên, bạn chỉ cần sử dụng các nhà xây dựng bản sao cho A để thiết lập một trạng thái ban đầu cho rằng một phần của đối tượng B, sau đó sửa chữa nó lên sau đó:

class B1 : public A 
{ 
public: 
    B1() : A(), z(0) {} 
    B1(const A& item) : A(item), z(1) { 
    // fix up the A sub-object that was copy constructed 
    // not quite the way we wanted 
    x = y; 
    y = 0; 
    } 
private: 
    int z; 
}; 

Tôi thấy rằng vô cùng khó hiểu và có lẽ rất dễ bị lỗi (giả sử rằng chúng ta muốn đối tượng phụ A trong đối tượng B khác với đối tượng A được truyền cho hàm tạo - một tình huống bất thường, nhưng đó là điều được đưa ra trong vấn đề).Tuy nhiên, thực tế là nó có thể được thực hiện cho một số biện minh cho các ví dụ lật đổ hơn mà theo sau ...

Ví dụ tiếp theo tạo đối tượng B tạm thời có bản sao chính xác đối tượng A mà chúng tôi muốn truy cập. Sau đó chúng tôi có thể sử dụng đối tượng tạm thời B để có được các mục được bảo vệ:

class B2 : public A 
{ 
public: 
    B2() : A(), z(0) {} 
    B2(const A& item) : A(), z(1) { 
    // create a special-use B2 object that can get to the 
    // parts of the A object we want access to 
    B2 tmp(item, internal_use_only); 

    x = tmp.y; // OK since tmp is of type B 
    } 

private: 
    int z; 

    // create a type that only B2 can use as a 
    // 'marker' to call a special constructor 
    // whose only purpose in life is to create 
    // a B object with an exact copy of another 
    // A sub-object in it 
    enum internal_use { 
    internal_use_only 
    }; 
    B2(const A& item, internal_use marker) : A(item), z(0) {}; 
}; 

tôi tìm giải pháp mà là một chút ít khó hiểu hơn là người đầu tiên, nhưng nó vẫn còn khó hiểu (theo ý kiến ​​của tôi). Có một phiên bản bastard của đối tượng B chỉ để đến các phần của đối tượng A mà chúng ta muốn là lẻ.

Chúng tôi có thể làm điều gì đó về điều đó bằng cách tạo một proxy đặc biệt cho A các đối tượng cung cấp quyền truy cập mà chúng tôi muốn. Lưu ý rằng đây là giải pháp 'lật đổ nhiều nhất' vì đó là điều mà bất kỳ lớp nào cũng có thể thực hiện để truy cập vào các phần được bảo vệ của A, ngay cả khi chúng không phải là các lớp con của chính bản thân họ là A. Trong trường hợp của lớp B, có một số tính hợp pháp để truy cập các bộ phận được bảo vệ của A đối tượng, vì B là một A và như chúng tôi đã thấy có cách giải quyết khác cho phép chúng tôi có quyền truy cập chỉ sử dụng các quyền đó class B có, vì vậy tôi xem xét điều này một phiên bản sạch hơn của những cách giải quyết trong trường hợp của class B.

class B3 : public A 
{ 
public: 
    B3() : A(), z(0) {} 
    B3(const A& item) : A(), z(1) { 
    // a special proxy for A objects that lets us 
    // get to the parts of A we're interested in 
    A_proxy tmp(item); 
    x = tmp.get_y(); 
    } 

private: 
    int z; 

    class A_proxy : public A 
    { 
    public: 
     A_proxy(const A& other) : A(other) {}; 
     int get_x() {return x;}; 
     int get_y() {return y;}; 
    }; 

}; 
+0

Cảm ơn câu trả lời này. Thật vậy, giả định của bạn là chính xác. Tôi muốn đối tượng phụ A trong đối tượng B khác với đối tượng A được truyền cho hàm tạo. –

+0

Trong kịch bản thực tế đời sống, tất cả thông tin tôi cần để sao chép đã có sẵn thông qua giao diện công cộng của A vì vậy tôi chỉ đơn giản sử dụng thay vì truy cập các thành viên được bảo vệ. Trên thực tế, thành viên A được bảo vệ ban đầu là riêng tư và tôi đã bảo vệ nó để có thể truy cập nó từ B. –

+0

@Michael Burr - Có cách nào để thay đổi thành viên được bảo vệ của A không (không phải bản sao A khi bạn đã hiển thị)? – Liton

Các vấn đề liên quan