2011-09-12 11 views
12

Gần đây tôi đã học được các con trỏ được quản lý và chạy vào kịch bản sau đây.C++ shared_ptr của đối tượng chồng

Tôi đang triển khai lớp mô hình/bộ điều khiển cho chế độ xem trò chơi. Quan điểm của tôi, sẽ hiển thị mọi thứ trong mô hình. Khá thẳng về phía trước. Trong chức năng chính của tôi, tôi nhanh chóng cả ba như thế này:

RenderModel m; 
m.AddItem(rect); // rect gets added just fine, it's an "entity" derivee 
RenderView v; 
v.SetModel(m); 

tôi render xem lớp khá đơn giản:

class RenderView 
{ 
public: 
explicit RenderView(); 
~RenderView(); 

void Update(); 

void SetModel(RenderModel& model); 

private: 
// disable 
RenderView(const RenderView& other); 
RenderView& operator=(const RenderView& other); 

// private members 
boost::scoped_ptr<RenderModel> _model; 
}; 

Việc thực hiện cho setview là khá chuẩn:

void RenderView::SetModel(RenderModel& model) 
{ 
    _model.reset(&model); 
} 

Các chính là, khung nhìn lưu trữ một mô hình trong một con trỏ thông minh. Tuy nhiên trong chính, mô hình được phân bổ trên stack. Khi chương trình thoát, bộ nhớ sẽ bị xóa hai lần. Điều này thật ý nghĩa. Sự hiểu biết hiện tại của tôi cho tôi biết rằng bất cứ thứ gì được lưu trữ trong smart_ptr (dưới bất kỳ hình thức nào) đều không được cấp phát trên ngăn xếp.

Sau khi tất cả các thiết lập ở trên, câu hỏi của tôi rất đơn giản: làm thế nào để tôi ra lệnh rằng một tham số không được cấp phát trên ngăn xếp? Đang chấp nhận một con trỏ thông minh như một tham số giải pháp duy nhất? Thậm chí sau đó tôi không thể đảm bảo rằng một người nào đó sử dụng lớp quan điểm của tôi không thể làm điều gì đó không chính xác như:

// If I implemented SetModel this way: 
void RenderView::SetModel(const std::shared_ptr<RenderModel>& model) 
{ 
    _model.reset(&*model); 
} 

RenderModel m; 
RenderView v; 
std::shared_ptr<RenderModel> ptr(&m); // create a shared_ptr from a stack-object. 
v.SetModel(ptr); 

Trả lời

8

làm thế nào để ra lệnh rằng một tham số không được cấp phát trên stack?

Có, yêu cầu người gọi cung cấp std::shared_ptr<RenderModel>. Nếu người gọi hiểu sai số std::shared_ptr, đó là vấn đề của người gọi chứ không phải của bạn.

Nếu bạn có ý định cho một RenderView để là chủ sở hữu duy nhất của một đặc biệt RenderModel, hãy xem xét có chức năng tham gia một std::unique_ptr hoặc std::auto_ptr thay; theo cách này, người gọi không nên giữ quyền sở hữu đối tượng sau khi nó gọi hàm.

Ngoài ra, nếu RenderModel là rẻ để sao chép, tạo một bản sao của nó và sử dụng các bản sao:

_model.reset(new RenderModel(model)); 
+0

Nếu người gọi không hiểu sai shared_ptr, tôi cho rằng không có cách nào để phát hiện một cái gì đó như thế này? Tôi hỏi đơn giản, bởi vì tôi cảm thấy tôi có thể mắc lỗi này lần nữa, và tôi không muốn bỏ ra hàng giờ để gỡ lỗi lại vấn đề. Tôi có thể để lại cho tôi một lưu ý dính trên màn hình của tôi cho đến khi nó cháy vào đầu của tôi ... – Short

+0

Có một số 'thủ thuật' đã biết để phát hiện xem một đối tượng có trong ngăn xếp hay vùng heap hay không, như so sánh địa chỉ. Tất cả các chuyển tiếp trên hành vi phụ thuộc không xác định hoặc thực hiện. Tôi đoán nếu nó chỉ để gợi ý cho mình nó có thể làm việc, nhưng họ không phải là một giải pháp thực sự. –

+3

Không, nếu hàm của bạn có một 'std :: shared_ptr', không có cách nào để biết con trỏ đó trỏ đến một đối tượng hợp lệ hay không. Điều đó nói rằng, nếu nó rõ ràng có một 'std :: shared_ptr', nó khó khăn hơn rất nhiều (mã xây dựng một' std :: shared_ptr' từ một biến cục bộ có vẻ sai ở cái nhìn đầu tiên và dễ tránh). –

3

Bạn có lẽ nên xác định ngữ nghĩa của lớp học của bạn rõ ràng hơn. Nếu bạn muốn RenderView là chủ sở hữu của RenderModel, nó sẽ tự tạo nó (có thể nhận được trong hàm tạo một số định danh để sử dụng với một nhà máy).

Tôi đã nhìn thấy các lớp nhận quyền sở hữu đối tượng và xác định rõ ràng rằng đối tượng này phải nằm trên heap, nhưng điều này, theo ý kiến ​​của tôi, dễ bị lỗi, giống như lỗi bạn gặp phải. Bạn không thể cung cấp cho một đối tượng ngăn xếp cho một con trỏ thông minh mà hy vọng nó được trên đống (vì nó sẽ sử dụng xóa trên nó khi nó muốn làm sạch nó).

+0

Qt sử dụng mô hình trên, đó là nơi tôi có ý tưởng. Nó cho phép một số khung nhìn được liên kết với một mô hình. Tôi sẽ phải xem xét kỹ hơn việc triển khai của họ. – Short

2

Cách bạn mô tả những gì bạn muốn làm là hoàn toàn sai. Trong mẫu thiết kế MVP, khung nhìn không nên truy cập trực tiếp vào mô hình, nhưng nên gửi các lệnh tới trình dẫn (bằng cách gọi các hàm của người trình bày).

Dù sao, người kia đã trả lời câu hỏi của bạn: đối tượng mô hình của bạn đã được cấp phát trên heap, như thế này:

std::shared_ptr<RenderModel> ptr(new RenderModel); 
RenderView v; 
v.SetModel(ptr); 

khác đối tượng shared_ptr của bạn sẽ cố gắng xóa một đối tượng ngăn xếp.

1

Bạn nên quay lại và suy nghĩ về thiết kế. Mùi mã đầu tiên là bạn đang lấy một đối tượng bằng cách tham chiếu và sau đó cố gắng quản lý nó như một con trỏ thông minh. Đó là sai.

Bạn nên bắt đầu bằng cách quyết định ai chịu trách nhiệm về tài nguyên và thiết kế xung quanh, nếu RenderView chịu trách nhiệm quản lý tài nguyên, thì không nên chấp nhận tham chiếu mà là một con trỏ thông minh. Nếu đó là chủ sở hữu duy nhất, chữ ký phải có một số std::unique_ptr (hoặc std::auto_ptr nếu trình biên dịch + libs của bạn không hỗ trợ unique_ptr), nếu quyền sở hữu bị pha loãng (muốn tạo chủ sở hữu duy nhất bất cứ khi nào có thể), sau đó sử dụng shared_ptr.

Nhưng cũng có các trường hợp khác trong đó RenderView không cần quản lý tài nguyên, trong trường hợp này, có thể lấy mô hình bằng cách tham chiếu và lưu nó theo tham chiếu nếu không thể thay đổi trong suốt thời gian RenderView. Trong trường hợp này, trong đó RenderView không chịu trách nhiệm quản lý tài nguyên, nó không nên cố gắng delete nó bao giờ (bao gồm thông qua một con trỏ thông minh).

1
class ModelBase 
{ 
    //..... 
}; 

class RenderView 
{ 
    //.......... 
public: 
    template<typename ModelType> 
    shared_ptr<ModelType> CreateModel() { 
     ModelType* tmp=new ModelType(); 
     _model.reset(tmp); 
     return shared_ptr<ModelType>(_model,tmp); 
    } 

    shared_ptr<ModelBase> _model; 
    //...... 
}; 

Nếu trình tạo lớp mô hình có tham số, có thể thêm thông số vào phương thức CreateModel() và sử dụng kỹ thuật chuyển tiếp hoàn hảo C++ 11.

1

Bạn nên yêu cầu người dùng nhập đúng cách. Đầu tiên thay đổi kiểu đầu vào của bạn thành con trỏ thông minh (thay vì biến tham chiếu). Thứ hai có họ vượt qua đúng con trỏ thông minh với một mà không làm bất cứ điều gì (NoDelete dưới đây, ví dụ).

Phương pháp tấn công sẽ là kiểm tra phân đoạn bộ nhớ. Ngăn xếp luôn luôn sẽ phát triển xuống từ không gian hạt nhân (tôi nghĩ 0xC0000000 trong 32 bit, một cái gì đó giống như 0x7fff2507e800 trong 64 bit, dựa trên mã dưới đây). Vì vậy, bạn có thể đoán dựa trên vị trí bộ nhớ cho dù đó là một biến stack hay không. Mọi người sẽ cho bạn biết nó không phải là di động, nhưng nó là loại, trừ khi bạn sẽ có công cụ triển khai trên các hệ thống nhúng.

#include <iostream> 
#include <memory> 

using namespace std; 

class foo 
{ 
    public: 
    foo(shared_ptr<int> in) { 
     cerr << in.get() << endl; 
     cerr << var.use_count() << endl; 
     var = in; 
     cerr << var.use_count() << endl; 
    }; 

    shared_ptr<int> var; 
}; 

struct NoDelete { 
    void operator()(int* p) { 
     std::cout << "Not Deleting\n"; 
    }; 
}; 

int main() 
{ 
    int myval = 5; 

    shared_ptr<int> valptr(&myval, NoDelete()); 
    foo staticinst(valptr); 

    shared_ptr<int> dynptr(new int); 
    *dynptr = 5; 
    foo dynamicinst(dynptr); 
} 
0

Tóm lại: xác định deleter tùy chỉnh. Trong trường hợp của một con trỏ thông minh trên một đối tượng ngăn xếp, bạn có thể xây dựng con trỏ thông minh với một deleter tùy chỉnh, trong trường hợp này là "Null Deleter" (hoặc "StackObjectDeleter")

class StackObjectDeleter { 
public: 
    void operator() (void*) const {} 
}; 

std::shared_ptr<RenderModel> ptr(&m, StackObjectDeleter()); 

Các StackObjectDeleter thay thế default_delete như đối tượng deleter. default_delete chỉ đơn giản là các cuộc gọi delete (hoặc delete []). Trong trường hợp StackObjectDeleter, sẽ không có gì xảy ra.

+0

holy necro batman! – Short