2015-07-14 18 views
6

Tôi muốn lưu trữ các đối tượng của các lớp bắt nguồn từ một giao diện chung (lớp trừu tượng) trong một std :: vector của lớp trừu tượng đó. Vector này nên được điền vào một vòng lặp và thường tôi sẽ gọi hàm tạo của một lớp và đẩy đối tượng đã tạo vào vectơ.Thêm các phần tử vào std :: vector của lớp trừu tượng

Như tôi đã hiểu, trong trường hợp của một lớp trừu tượng, tôi chỉ có thể lưu trữ con trỏ đến lớp đó, vì vậy tôi cần phải push_back con trỏ của các lớp dẫn xuất. Tuy nhiên, tôi không chắc chắn về phạm vi của các đối tượng mới được tạo ra này.

Vui lòng xem mã bên dưới. Mã này biên dịch và hoạt động tốt nhưng câu hỏi của tôi là:

a) Các đối tượng có được bảo đảm tồn tại trong vòng lặp thứ hai trong chức năng chính không? Hoặc họ có thể ngừng tồn tại vượt quá phạm vi của vòng lặp mà chúng được tạo ra không?

b) Có phải tất cả các đối tượng hủy hoại được gọi là hoặc có thể có rò rỉ bộ nhớ không?

#include<vector> 
#include<iostream> 
class Interface { 
    public: 
    Interface(int y) : x(y) {} 
    virtual ~Interface() {} 
    virtual void f() = 0; 
    int x; 
}; 

class Derived_A : public Interface { 
    public: 
    Derived_A(int y) : Interface(y) {} 
    void f(){ return; } 
}; 

class Derived_B : public Interface { 
    public: 
    Derived_B(int y) : Interface(y) {} 
    void f(){ return; } 
}; 


int main() 
{ 
    std::vector<Interface*> abstractObjects; 
    int N = 5; 
    for(int ii = 0; ii < N; ii++) 
    { 
     abstractObjects.push_back(new Derived_A(ii)); 
     abstractObjects.push_back(new Derived_B(ii)); 
    } 

    for(int ii = 0; ii < abstractObjects.size(); ii++) 
    { 
     abstractObjects[ii]->f(); 
     std::cout << abstractObjects[ii]->x << '\t' << std::endl; 
    } 


    for(int ii = 0; ii < abstractObjects.size(); ii++) 
    { 
     delete abstractObjects[ii]; 
    } 

    return 0; 
} 

Trả lời

7

Đây là một trường hợp hoàn hảo cho con trỏ thông minh . Bạn có thể lưu trữ các con trỏ trong một unique_ptr là một loại RAII. Khi unique_ptr hết phạm vi, nó sẽ tự động xóa bộ nhớ cho bạn.

//... 
    std::vector<std::unique_ptr<Interface>> abstractObjects; 
    int N = 5; 
    for(int ii = 0; ii < N; ii++) 
    { 
     abstractObjects.push_back(std::make_unique<Derived_A>(ii)); 
     abstractObjects.push_back(std::make_unique<Derived_B>(ii)); 
    } 

    for(auto & e : abstractObjects) // ranged based for loop 
    { 
     e->f(); 
     std::cout << e->x << '\t' << std::endl; 
    } 
    // no need to do anything here. the vector will get rid of each unique_ptr and each unique_ptr will delete each pointer 
    return 0; 
} 
+0

Nếu sử dụng C++ 14 thì tại sao không cho vòng lặp phạm vi? – Slava

+0

@Slava Cảm ơn bạn đã đề xuất. Tôi đã chỉnh sửa mã. – NathanOliver

+0

Cảm ơn! Điều gì sẽ xảy ra nếu tôi có một hàm 'f2()' tạo ra vectơ như bạn gợi ý và sau đó tạo một đối tượng 'Obj (std :: vector >)' bằng cách sử dụng vectơ chính xác này và trả về 'Obj này 'từ chức năng? Liệu vectơ (và các phần tử của nó) vẫn tồn tại trong hàm gọi là 'f2() '? – seyfe

1

Có các đối tượng vẫn còn tồn tại ở bên ngoài phạm vi của for vòng đầu tiên của bạn, do đó bạn là đúng để delete họ.

Không phải không phải tất cả các đối tượng hủy được tự động gọi. Nếu bạn new thứ gì đó, thì bạn phải delete nó. Bạn có thể (và nên) tuy nhiên sử dụng con trỏ thông minh.

std::vector<std::unique_ptr<Interface>> abstractObjects; 
int N = 5; 
for(int ii = 0; ii < N; ii++) 
{ 
    abstractObjects.push_back(std::make_unique<Derived_A>(ii)); 
    abstractObjects.push_back(std::make_unique<Derived_B>(ii)); 
} 

Bây giờ bạn không cần phải delete bất cứ điều gì, destructors sẽ được gọi khi vector rơi ra khỏi phạm vi (và do đó để làm tất cả các yếu tố của nó)

4

Hãy để tôi giải quyết điểm của bạn.

a) Các đối tượng có được bảo đảm tồn tại trong vòng lặp thứ hai trong chức năng chính không? Hoặc họ có thể ngừng tồn tại vượt quá phạm vi của vòng lặp mà chúng được tạo ra không?

Khi bạn gọi từ khóa new, các đối tượng sẽ tồn tại cho đến khi bạn explictly gọi delete vào chúng để giải phóng bộ nhớ liên quan. Nếu bạn đã tạo ra các đối tượng trên stack, chúng sẽ rơi ra khỏi phạm vi sau khi vòng lặp đầu tiên kết thúc.

b) Có phải tất cả các đối tượng hủy hoại được gọi là hoặc có thể có rò rỉ bộ nhớ không?

Có, bạn đang gọi đúng trình phá hủy của từng đối tượng trong vòng lặp cuối cùng và thường sẽ không bị rò rỉ bộ nhớ. Tuy nhiên, nếu một ngoại lệ được ném trước khi bạn đến vòng lặp cuối cùng, bộ nhớ được cấp sẽ không được khôi phục và bạn sẽ bị rò rỉ. Xem this post.

Bạn có thể, tuy nhiên, cải thiện mã của bạn bằng cách sử dụng con trỏ thông minh, giải quyết vấn đề đó bằng cách tự động khôi phục bộ nhớ. Sử dụng std::make_unique<Derived_A>(ii) thay vì new Derived_A(ii) và khi vectơ nằm ngoài phạm vi, nó sẽ tự động giải phóng bộ nhớ được liên kết cho mỗi đối tượng chứa, loại bỏ nhu cầu tự gọi chính phá hủy trong vòng lặp cuối cùng.

+1

"và không nên có rò rỉ bộ nhớ." có lẽ bạn nên đề cập đến ngoại lệ đó cũng có thể dẫn đến rò rỉ bộ nhớ và đó là một lý do khác để sử dụng con trỏ thông minh. – Slava

+0

Có, bạn khá chính xác. Tôi sẽ chỉnh sửa ngay bây giờ. –

0

Tôi khuyên bạn nên sử dụng vector của con trỏ thông minh (thay vì con trỏ sở hữu thô). Trong khi số liệu quan sát con trỏ vẫn ổn, việc có một vectơ là số liệu sở hữu con trỏ có thể là nguồn rò rỉ tiềm ẩn.

Đối với quyền sở hữu không chia sẻ, hãy xem xét sử dụng vector<unique_ptr<Interface>> và sử dụng std::make_unique để tự động tạo mục mới sẽ được thêm vào vectơ.

Sử dụng một vector của gợi ý thông minh cho phép bạn viết mã đơn giản và rõ ràng hơn, kể từ khi dọn dẹp là tự động nhờ done để destructors C++ (nói cách khác, tất cả các mã ngẫu nhiên của nhãn hiệu yêu cầu với một vector của các con trỏ sở hữu thô chỉ biến mất).

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