2012-02-17 29 views
13

Dường như mỗi khi tôi thêm một đối tượng vào vector m_test, phương thức destructor được gọi. Tui bỏ lỡ điều gì vậy? Làm thế nào tôi có thể ngăn chặn điều này xảy ra?Tại sao destructor của lớp của tôi được gọi khi tôi thêm các cá thể vào một vectơ?

class TEST 
{ 
public: 
    TEST(); 
    ~TEST(); 
    int * x; 
}; 

TEST::TEST() 
{ 
} 

TEST::~TEST() 
{ 
... it is called every time I push_back something to the vector ... 
    delete x; 
} 

    vector<TEST> m_test; 
    for (unsigned int i=0; i<5; i++) 
    { 
     m_test.push_back(TEST()); 
    } 
+2

Trong C++ 11, bạn có thể sử dụng 'm_test.emplace_back()' để tránh tạo tạm thời. Trong mọi trường hợp, hãy luôn nhớ [Quy tắc Ba] (http://stackoverflow.com/questions/4172722). –

Trả lời

9

Vấn đề ở đây là bạn đang vi phạm các Rule of Three. Lớp của bạn có một destructor, do đó bạn cần một copy-constructor và một toán tử gán. Ngoài ra, bạn không thể cho phép sao chép lớp học của mình (ví dụ bằng cách tạo T(T const&)T& operator=(T const&) riêng tư hoặc bằng cách lấy từ boost::noncopyable), rồi đổi kích thước véc tơ thay vì sử dụng push_back.

Trong trường hợp đầu tiên, bạn chỉ có thể push_back lớp học của mình như thường lệ.Trong phần thứ hai, cú pháp sẽ là một cái gì đó giống như

std::vector<TEST> vec(5); 
// vec now has five default-constructed elements of type TEST. 

Không làm một trong những điều này là một ý tưởng tồi, vì bạn rất có khả năng chạy vào các vấn đề xóa đôi tại một số điểm - ngay cả khi bạn nghĩ rằng bạn' sẽ không bao giờ sao chép hoặc chỉ định một TEST trong đó x != nullptr, an toàn hơn rất nhiều để cấm rõ ràng.

Bằng cách này, nếu bạn có con trỏ thành viên đó sẽ bị xóa khi một đối tượng đi ra khỏi phạm vi, xem xét sử dụng con trỏ thông minh như scoped_ptr, unique_ptrshared_ptr (và có thể auto_ptr nếu bạn không thể sử dụng Boost hoặc C + +11).

+0

Cảm ơn bạn Anton, tôi mới đến lập trình C++, và không bao giờ nghe nói về Quy tắc Ba. Cảm ơn bạn đã chỉ cho tôi đúng hướng. – 2607

7

Nó không gọi khi bạn push_back, nó được gọi là khi tạm thời bị phá hủy.

Để khắc phục nó trong ví dụ của bạn:

TEST test; 
for (int i = 0; i < 5; ++i) 
{ 
    m_test.push_back(test); 
} 

chỉ nên gọi nó là một lần.

Mã của bạn đang tạo tạm thời TEST trong vòng lặp, sử dụng nó trong push_back, khi đó tạm thời sẽ hết phạm vi khi vòng lặp kết thúc/lặp lại và bị hủy. Điều đó xảy ra chính xác như mong muốn, vì nhu cầu tạm thời TEST cần được dọn sạch.

Nếu bạn muốn tránh điều đó, bạn cần phải làm bất cứ điều gì khác ngoài việc tạo một đối tượng tạm thời cho mỗi lần đẩy. Một giải pháp tiềm năng là:

vector<TEST> m_test(5); // Note reserving space in the vector for 5 objects 

std::fill(m_test.begin(), m_test.end(), TEST()); // Fill the vector with the default ctor 

Tùy thuộc vào cách STL của bạn được tối ưu hóa, điều này có thể không cần phải tạo nhiều bản sao.

Bạn cũng có thể để có được xử lý tốt hơn nếu bạn thực hiện một constructor sao chép trong lớp TEST của bạn, như:

TEST::TEST(const TEST & other) 
{ 
    x = new int(*other.x); // Not entirely safe, but the simplest copy ctor for this example. 
} 

Cho dù điều này là thích hợp, hoặc làm thế nào bạn xử lý nó, phụ thuộc vào lớp học của bạn và nó nhu cầu, nhưng bạn thường có một hàm tạo bản sao khi bạn đã định nghĩa hàm tạo và hàm hủy thường xuyên của riêng bạn (nếu không trình biên dịch sẽ tạo ra một trình tạo và trong trường hợp này, nó sẽ dẫn đến sao chép và treo con trỏ đến x).

1

vector.push_back() sao chép đối tượng đã cho vào vùng lưu trữ của nó. Đối tượng tạm thời bạn đang xây dựng trong cuộc gọi push_back() bị hủy ngay sau khi được sao chép và đó là những gì bạn đang thấy. Một số trình biên dịch có thể có khả năng tối ưu hóa bản sao này đi, nhưng bạn dường như không thể.

1

Trong m_test.push_back(TEST());, TEST() sẽ tạo biến tạm thời. Sau khi vector sao chép nó vào bộ nhớ riêng của nó, biến tạm thời bị hủy.

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

vector<TEST> m_test(5, TEST()); 
0

Để tránh việc hủy tạm thời để tránh các nhà thầu sao chép, hãy cân nhắc sử dụng vector::resize hoặc vector::emplace_back. Dưới đây là ví dụ sử dụng emplace_back:

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) 
{ 
    m_test.emplace_back(); 
} 

Phần tử vectơ sẽ được xây dựng tại chỗ mà không cần sao chép. Khi vt bị hủy, mỗi phần tử vectơ sẽ tự động bị hủy.

C++ 0x là bắt buộc (sử dụng -std=c++0x với gnu). #include <vector> là tất nhiên cũng cần thiết.

Nếu một constructor mặc định không được sử dụng (ví dụ, nếu TEST::x là một tham chiếu thay vì một con trỏ), chỉ cần thêm đối số cho cuộc gọi đến emplace_back() như sau:

class TEST 
{ 
public: 
    TEST(int & arg) : x(arg) {;} // no default constructor 
    int & x; // reference instead of a pointer. 
}; 

. . . 

int someInt; 

vector<TEST> m_test; 
m_test.reserve(5); 
for (uint i=0; i<5; i++) { 
    m_test.emplace_back(someInt); // TEST constructor args added here. 
} 

Các reserve() thể hiện là không bắt buộc nhưng đảm bảo rằng có đủ không gian trước khi bắt đầu xây dựng các phần tử vectơ.

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