2009-09-12 14 views
11

Tôi bối rối: Tôi nghĩ dữ liệu được bảo vệ đã được đọc/ghi bởi con của một lớp đã cho trong C++.Dữ liệu được bảo vệ trong lớp cha không có sẵn trong lớp con?

Đoạn dưới đây thất bại trong việc biên dịch trong MS Compiler

class A 
{ 
protected: 
    int data; 
}; 

class B : public A 
{ 
    public: 

    B(A &a) 
    { 
    data = a.data; 
    } 
}; 

int main() 
{ 
    A a; 
    B b = a; 
    return 0; 
} 

Thông báo lỗi:

Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 15.00.30729.01 for 80x86 
Copyright (C) Microsoft Corporation. All rights reserved. 

demoFail.cpp 
demoFail.cpp(12) : error C2248: 'A::data' : cannot access protected member declared in class 'A' 
     demoFail.cpp(4) : see declaration of 'A::data' 
     demoFail.cpp(2) : see declaration of 'A' 

Tôi đang làm gì sai?

Trả lời

10

Theo TC++ PL, pg 404:

Một lớp học có nguồn gốc có thể truy cập vào một lớp cơ sở bảo vệ các thành viên chỉ cho các đối tượng của loại hình riêng của mình .... Điều này ngăn cản các lỗi tinh vi mà nếu không sẽ xảy ra khi một lớp dẫn xuất bị hỏng dữ liệu thuộc về các lớp dẫn xuất khác.

Tất nhiên, đây là một cách dễ dàng để sửa lỗi này đối với trường hợp của bạn:

class A 
{ 
protected: 
    int data; 
}; 

class B : public A 
{ 
public: 
    B(const A &a) 
     : A(a) 
    { 
    } 
}; 

int main() 
{ 
    A a; 
    B b = a; 
    return 0; 
} 
+0

Umph. Để được chính xác, trường hợp này là một trường hợp giả chỉ để chứng minh vấn đề. Tôi thực sự muốn thực hiện một số dữ liệu được đọc khá sâu. –

+0

Trong mọi trường hợp, bạn chỉ có thể muck xung quanh với dữ liệu được bảo vệ trong phần A hoặc đối tượng B khác. Nếu bạn có một cá thể A hoặc một cá thể C cũng có nguồn gốc từ A, B không có quyền đặc biệt đối với những người đó. Nếu bạn không thể thực hiện lời khuyên này (hãy tạo một phần B này bằng cách sao chép nó thành phần cơ sở của B này, mà bạn có quyền truy cập), thì bạn có thể gặp vấn đề về thiết kế có thể yêu cầu bức tranh lớn hơn câu trả lời. – UncleBens

+0

@rlbond: Mã hóa sau khi đi ngủ lần nữa. Điều này đã giải quyết được vấn đề của tôi. Cảm ơn. :) –

0

Phương thức khởi tạo của B là riêng tư. Nếu bạn không chỉ định bất cứ điều gì, trong một lớp, công cụ sửa đổi mặc định là riêng tư (đối với các cấu trúc là công khai). Vì vậy, trong ví dụ này, vấn đề là bạn không thể xây dựng B. Khi bạn thêm công khai vào vấn đề anotator B của nhà xây dựng phát sinh:

B có quyền sửa đổi phần A từ nguồn gốc này chứ không phải khác A như trong trường hợp này.

Bạn có thể làm như sau:

class A 
{ 
public: 
    A() 
     : data(0) 
    { 
    } 
    A(A &a) 
    { 
    data = a.data; 
    } 
protected: 
    int data; 
}; 

class B : public A 
{ 
public: 
    B(A &a) 
     : A(a) 
    { 
    } 
}; 

int main() 
{ 
    A a; 
    B b = a; 
    return 0; 
} 
+0

Đó là những gì tôi nghĩ nhưng nó không phải là vấn đề. – rlbond

+0

@jde: đó là một lỗi đánh máy, thực sự. Làm cho hàm tạo của công chúng B tạo ra cùng một lỗi. –

+0

@Paul, vui lòng sửa bất kỳ lỗi chính tả nào trong câu hỏi, vì vậy các câu trả lời khác không đề xuất cùng một giải pháp :) –

2

CáC++ Chuẩn C nói về các thành viên không tĩnh được bảo vệ tại 11.5/1

Khi một người bạn hoặc một Hàm thành viên của lớp dẫn xuất tham chiếu đến hàm thành viên không tĩnh được bảo vệ hoặc thành viên dữ liệu không được bảo vệ của lớp cơ sở, kiểm tra truy cập được áp dụng ngoài những điều được mô tả trước đó trong khoản 11. Ngoại trừ khi tạo thành con trỏ tới thành viên (5.3.1), truy cập e phải thông qua một con trỏ tới, tham chiếu đến, hoặc đối tượng của lớp dẫn xuất (hoặc bất kỳ lớp nào xuất phát từ lớp đó) (5.2.5). Nếu truy cập là để tạo thành một con trỏ đến thành viên, thì tên lồng nhau-specifier sẽ đặt tên cho lớp dẫn xuất (hoặc bất kỳ lớp nào có nguồn gốc từ lớp đó).

Ngoài việc sửa chữa những điều được đề cập trước đó bởi những người khác (constructor B là riêng tư), tôi nghĩ cách của rlbond sẽ làm tốt. Tuy nhiên, một hậu quả trực tiếp của các đoạn trên của Tiêu chuẩn là sau đây là có thể sử dụng một con trỏ thành viên, được cho rằng là một cái lỗ trên hệ thống kiểu, tất nhiên

class B : public A { 
public: 
    B(A &a){ 
    int A::*dataptr = &B::data; 
    data = a.*dataptr; 
    } 
}; 

Tất nhiên, mã này không được khuyến khích để làm, nhưng cho thấy rằng bạn có thể truy cập nó, nếu bạn thực sự cần phải (tôi đã nhìn thấy cách này được sử dụng để in ra một std::stack, std::queue, std::priority_queue bằng cách truy cập thành viên chứa bảo vệ của nó c)

+0

hi Johannes Schaub - litb có thể bạn vui lòng chỉ cho tôi làm thế nào để truy cập được bảo vệ thành viên c trong std :: stack hoặc std :: đợi tôi có vấn đề với việc tiếp cận nó nó đã cho tôi một lỗi C2248: 'std :: stack <_Ty> :: c': không thể truy cập thành viên được bảo vệ được khai báo trong lớp 'std :: stack <_Ty>' thanks –

1

bạn chỉ không nên sao chép số A đối tượng trong một hàm tạo B.Mục đích là để việc khởi tạo các thành viên của A thành công ty xây dựng riêng của nó:

struct A { 
    A(const A& a): data(a.data) {} 
    protected: int data; 
}; 

struct B : public A { 
    B(const A& a): A(a) {} 
};