2011-08-25 35 views
12

Xin lỗi vì tiêu đề dài nhưng tôi muốn cụ thể. Tôi dự kiến ​​mã sau đây sẽ hoạt động nhưng không và tôi không thể hiểu tại sao:/Làm thế nào đến một con trỏ đến một lớp dẫn xuất không thể được chuyển đến một hàm mong đợi một tham chiếu đến một con trỏ đến lớp cơ sở?

#include <cstdio> 
#include <cassert> 

class UniquePointer 
{ 
public: 
    void Dispose() 
    { 
     delete this; 
    } 

    friend void SafeDispose(UniquePointer*& p) 
    { 
     if (p != NULL) 
     { 
      p->Dispose(); 
      p = NULL; 
     } 
    } 
protected: 
    UniquePointer() { } 
    UniquePointer(const UniquePointer&) { } 
    virtual ~UniquePointer() { } 
}; 

class Building : public UniquePointer 
{ 
public: 
    Building() 
    : mType(0) 
    {} 
    void SetBuildingType(int type) { mType = type; } 
    int GetBuildingType() const { return mType; } 
protected: 
    virtual ~Building() { } 
    int mType; 
}; 

void Foo() 
{ 
    Building* b = new Building(); 
    b->SetBuildingType(5); 
    int a = b->GetBuildingType(); 
    SafeDispose(b);  // error C2664: 'SafeDispose' : cannot convert parameter 1 from 'Building *' to 'UniquePointer *&' 
    b->Dispose(); 
} 

int main(int argc, char* argv[]) 
{ 
    Foo(); 
    return 0; 
} 
+1

Lưu ý rằng không có gì "an toàn" về những gì bạn đang cố gắng làm. Nếu 'b' được đặt thành' NULL' thì 'b-> Dispose();' gây ra hành vi giống như undefined như thể 'b' trỏ đến một đối tượng đã xóa. –

+0

@Charles: Đã chỉnh sửa, đang chờ xem xét. Lỗi này không phải là một phần của câu hỏi, phải không? –

+0

Ngoài ra, không cần 'SafeDispose' làm bạn vì' Dispose' là công khai. –

Trả lời

42

Hãy tưởng tượng nó là hợp pháp. Sau đó, bạn có thể viết mã như thế này:

class Animal : public UniquePointer 
{ 
}; 

void Transmogrify(UniquePointer*& p) 
{ 
    p = new Animal(); 
} 

void Foo() 
{ 
    Building* b = nullptr; 
    Transmogrify(b); 
    b->SetBuildingType(0); // crash 
} 

Quan sát bạn đã vi phạm loại hệ thống (bạn đặt Động vật ở nơi cần xây dựng) mà không yêu cầu bỏ hoặc lỗi trình biên dịch.

+0

Tôi thấy quan điểm của bạn. Lý do tôi muốn tham chiếu đến con trỏ là để có thể thiết lập con trỏ đó để NULL nhưng tôi rõ ràng là bỏ lỡ tất cả các tác dụng phụ này có thể có. –

3

Không được phép bởi vì nếu nó là bạn có thể làm như sau:

friend void SafeDispose(UniquePointer*& p) 
{ 
    p = new UniquePointer(); 
} 


Building* building; 
SafeDispose(building) 
//building points to a UniquePointer not a Building. 

Tôi đoán công việc xung quanh sẽ là một hàm template.

+0

Ngoại trừ việc bạn không thể khởi tạo con trỏ duy nhất, nó phải được phân lớp. Nhưng tôi thấy quan điểm của bạn. –

6

Tôi không nghĩ rằng nó có thể làm cho nó hoạt động theo cách bạn thiết kế nó. Thay vào đó, hãy thử như sau:

template <typename T> 
void SafeDispose(T * & p) 
{ 
    if (p != NULL) 
    { 
     p->Dispose(); 
     p = NULL; 
    } 
} 

class UniquePointer 
{ 
public: 
    void Dispose() 
    { 
     delete this; 
    } 

protected: 
    UniquePointer() { } 
    UniquePointer(const UniquePointer&) { } 
    virtual ~UniquePointer() { } 
}; 
+0

Tôi thích giải pháp của bạn, trong thực tế, bạn nên nói với lý do tại sao nó không thể làm điều đó theo cách tôi đã làm điều này sẽ có được câu trả lời chấp nhận của tôi. –

0

Tôi đoán SafeDispose của bạn có lẽ nên trông giống như:

friend void SafeDispose(UniquePointer** p) ... 

Để gọi nó bằng cách sử

SafeDispose(&(UniquePointer*)b); 

Sau đó, nó sẽ hoạt động theo cách này.

Nhưng tuyên bố tiếp theo của bạn

b->Dispose(); 

sẽ phá vỡ nguyên nhân b bây giờ sẽ được NULL, vì nó đã được xử lý và thiết lập để NULL bằng phương pháp SafeDispose của bạn.

+0

Nhưng '& b' là giá trị của loại' Tòa nhà ** 'mà bạn không thể chuyển đến một hàm lấy một' UniquePointer ** 'vì các lý do khá giống nhau mà bạn không thể liên kết một' Tòa nhà * 'với một -const 'UniquePointer * &'. –

+0

Cảm ơn bạn đã chỉ ra điều này. Thêm một diễn viên có lẽ sẽ khắc phục vấn đề này đúng không? (mã mẫu đã cập nhật). Sau đó, tôi đoán rằng câu trả lời wilx (bằng cách sử dụng một mẫu) có ý nghĩa hơn. –

+1

Không, dàn diễn viên không khắc phục được sự cố tạo ra sự cố mới. Các diễn viên chuyển đổi con trỏ nhưng kết quả của diễn viên là một rvalue và sau đó bạn không thể lấy địa chỉ của một rvalue. –

1

Để trả lời tiêu đề câu hỏi của bạn, bạn không thể ràng buộc tham chiếu không const tới cơ sở của một thể hiện lớp dẫn xuất vì sau đó bạn có thể đặt tham chiếu đó thành con trỏ tới cá thể cơ sở không phải là bắt nguồn. Xem xét chức năng này:

void Renew(UniquePointer *& p) { 
    delete p; 
    p = new UniquePointer(); 
} 

nếu bạn có thể vượt qua nó một con trỏ đến Building bạn sẽ có thể thiết lập nó không chính xác để trỏ đến một trường hợp UniquePointer.

Như đã được đề xuất, giải pháp là thay đổi tham chiếu của bạn thành con trỏ đơn giản. Không chỉ điều này giải quyết vấn đề của bạn, nhưng nó cũng là một thực hiện tốt hơn của SafeDispose(); như bạn đã viết, hàm này đưa ra ý tưởng sai lầm rằng bạn sẽ luôn đặt thành 0 tất cả các trường hợp UniquePointer của bạn. Nhưng điều gì sẽ xảy ra nếu ai đó đã viết (giả sử UniquePointer constructor là công vì đơn giản):

UniquePointer *p1 = new UniquePointer(); 
UniquePointer *p2 = p1; 
SafeDispose(p1); 

Họ mong chờ tất cả các UniquePointer s của họ phải được thực hiện đúng cách chăm sóc, khi p2 thực sự là không hợp lệ.

+0

Tôi có thể đã cho mọi thứ một cái tên tốt hơn so với những gì tôi đã làm. Tôi dự định SafeDispose để thiết lập con trỏ đến NULL và kiểm tra NULL, nhưng tôi đã không xem xét thực tế là callee có thể có một bản sao của con trỏ. Nhưng điều quan trọng cần lưu ý là không có chia sẻ gì, tôi đã quan tâm đến một hệ thống "sở hữu độc quyền" sau khi đọc phong cách mã hóa của Google. –

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