2009-09-07 10 views
5

Tôi đã thực hiện ít chương trình sau: (cơ bản là một lớp mà couts nếu nó được tạo ra, sao chép hoặc bị phá hủy và một chính mà không một số trong đó)std C++ hủy diệt yếu tố container và hành vi chèn

class Foo 
{ 
public: 
Foo(string name): _name(name) 
{ 
    cout << "Instance " << _name << " of Foo created!" << std::endl; 
}; 
Foo(const Foo& other): _name(other._name) 
{ 
    cout << "Instance " << _name << " of Foo copied!" << std::endl; 
}; 

~Foo() 
{ 
    cout << "Instance " << _name << " of Foo destroyed!" << std::endl; 
} 
string _name; 
}; 



int main(int argc, char**argv) 
{ 
Foo albert("Albert"); 
Foo bert("Bert"); 
{ 
    vector<Foo> v1, v2; 
    system("PAUSE"); 

    v1.push_back(albert); 
    system("PAUSE"); 

    v2.push_back(bert); 
    system("PAUSE"); 

    v1 = v2; 
    system("PAUSE"); 
} 
    system("PAUSE"); 
} 

Kết quả trông giống như sau:

Instance Albert of class Foo created! 
Instance Bert of class Foo created! 
Press any key... 
Instance Albert of class Foo copied!  
Instance Albert of class Foo copied!  // why another copy? 
Instance Albert of class Foo destroyed! // and destruction? 
Press any key... 
Instance Bert of class Foo copied! 
Instance Bert of class Foo copied! 
Instance Bert of class Foo destroyed! 
Press any key...      // v1=v2 why did the albert instance not get destroyed? 
Press any key...      
Instance Bert of class A destroyed! 
Instance Bert of class A destroyed! 
Press any key...      // there's still an albert living in the void 

Điều này khiến tôi rất kỳ quặc. Tại sao tôi thậm chí bận tâm chuyển một cái gì đó như là một tài liệu tham khảo nếu nó được sao chép hai lần không? Tại sao v1.operator = (khác) không phá hủy các phần tử chứa? Nó sẽ phù hợp với hành vi của shared_ptr. Ai đó có thể cho tôi biết lý do không?

BỔ SUNG tôi đặt điều này trong một vòng lặp vô tận và kiểm tra việc sử dụng mem, nó dường như không để sản xuất rò rỉ mem ít nhất.

ADDITION Ok, mem không phải là vấn đề vì nó sử dụng toán tử = chứ không phải sao chép, ok cảm ơn. Khi tôi thêm

v1.reserve(10); 
v2.reserve(10); 

số lượng bản sao hợp lý diễn ra. mà không có nó phân bổ lại và sao chép toàn bộ vectơ cho mỗi push_back đơn, (mà tôi thấy khá chậm chạp ngay cả đối với các vectơ nhỏ). Nhìn này, tôi sẽ xem xét sử dụng .reserve hơn và tối ưu hóa toán tử gán của tôi Giống như địa ngục :)

ADDITION: TÓM TẮT

  1. Tất cả những vấn đề này dường như đặc trưng cho VC++ 2005.
  2. Nếu kích thước của hai thùng chứa khớp nhau, việc triển khai của tôi sử dụng toán tử = trên các yếu tố thay vì phá hủy các phần tử cũ và sao chép các phần tử mới. NẾU các kích thước khác nhau, phá hủy bình thường và sao chép được sử dụng.
  3. Với việc thực hiện năm 2005, người ta phải sử dụng dự trữ! Nếu không thì hoạt động tuân thủ tiêu chuẩn và không tương thích.
  4. Những hộp đen này đen hơn tôi tưởng.
+0

Bạn đã thử biên dịch bản này dưới dạng bản dựng chưa? – jalf

+0

Có. Cùng một kết quả. – AndreasT

+0

Nó rất dễ dàng để thử bản thân, chỉ cần sao chép dán vào một dự án trống rỗng, và thêm bao gồm iostream, vector và chuỗi, và đi. – AndreasT

Trả lời

4

Tại sao tôi thậm chí bận tâm chuyển thứ gì đó làm tài liệu tham khảo nếu nó được sao chép hai lần?

Bạn nên xem xét các loại vùng chứa STL làm hộp đen có thể sao chép các đối tượng bạn lưu trữ thường xuyên như chúng cần. Ví dụ, mỗi khi container được thay đổi kích thước, tất cả các đối tượng sẽ được sao chép.

Có thể việc triển khai trình biên dịch của bạn là push_back() sử dụng bản sao phụ tạm thời. Trên máy tính của tôi (gcc trên Mac OS X), không có thêm bản sao nào trong thời gian push_back() (theo đầu ra của chương trình của bạn).

Bản sao này xảy ra ở đâu đó trong mã STL, không phải trong trình tạo bản sao của bạn (vì nó sử dụng tham chiếu).

Tại sao v1.operator = (khác) không hủy các phần tử chứa?

Foo::operator= sẽ được gọi cho cá thể "albert" với đối tượng "bert" làm đối số. Do đó, không có hoạt động phá hủy và sao chép ngầm ở đây. Bạn có thể muốn xác minh điều này bằng cách cung cấp thực hiện của riêng bạn cho các nhà điều hành:

Foo& operator=(const Foo& other) { 
    cout << "Instance " << other._name << " of Foo assigned to " << _name << "!" << std::endl; 
    return *this; 
} 

này tạo ra đầu ra sau trên máy tính của tôi:

Instance Albert của Foo tạo ra!
Ví dụ Bert của Foo đã được tạo!
Phiên bản Albert của Foo được sao chép!
Instance Bert of Foo được sao chép!
Sơ thẩm Bert của Foo được giao cho Albert!
Ví dụ Bert của Foo bị phá hủy!
Ví dụ Albert của Foo bị phá hủy!
Ví dụ Bert của Foo bị phá hủy!
Ví dụ Albert của Foo bị phá hủy!

+1

Và hãy nhớ rằng chỉ cần theo dõi các bản sao có nghĩa là bản sao của bạn ctor trở nên phức tạp hơn đến điểm mà trình tối ưu hóa có thể không còn loại bỏ nó. Vì vậy, bạn đang tính các cuộc gọi ctor tồn tại chỉ vì chúng được tính! – MSalters

+0

Đối số hộp đen là hợp lệ, tuy nhiên, tôi luôn hy vọng đây không phải là các hộp đen ngu ngốc :). Còn một thất vọng M $ khác. – AndreasT

+0

@MSalters: Điều này thật thú vị. Nguyên tắc không chắc chắn của C++. – stribika

3

Có toán tử được tạo tự động =. Khi bạn làm v1 = v2 toán tử được sử dụng. Tại thời điểm đó, một trong các trường hợp "albert" trở thành "bert". Thử thêm chức năng này vào Foo:

Foo& operator = (const Foo& rval) { 
    cout << _name << " = " << rval._name << endl; 
    _name = rval._name; 
    return *this; 
} 

Điều này tương tự như được tạo tự động nhưng in ra thông báo lỗi để bạn có thể xem điều gì đang xảy ra.

+0

và những gì về các bản sao dư thừa và rò rỉ mem (mà thực sự không xảy ra, xem ở trên) – AndreasT

+1

Nếu bạn thêm chức năng này (hoặc bộ đếm tĩnh), bạn có thể thấy rằng không có rò rỉ. Đối với các bản sao phụ không có bảo đảm rằng một container STL sẽ chỉ sao chép mọi thứ một lần. Nó có thể là một thông qua giá trị hoặc một số điều khác tùy thuộc vào thực hiện STL của bạn. – stribika

1

Việc "sao chép kép" không xảy ra khi được biên dịch với GCC. Điều này phải được cụ thể cho cách std :: vector được thực hiện trong VC++.

+0

Có vẻ như cụ thể để VS2005 – AndreasT

1

Visual Studio 2008 mang lại cho tôi kết quả như sau:

Instance Albert of Foo created! 
Instance Bert of Foo created! 
Press any key to continue . . . 
Instance Albert of Foo copied! 
Press any key to continue . . . 
Instance Bert of Foo copied! 
Press any key to continue . . . 
Press any key to continue . . . << here auto-generated operator= doing its job 
Instance Bert of Foo destroyed! 
Instance Bert of Foo destroyed! << this is Albert was originally 
Press any key to continue . . .

Có vẻ là rằng std::vector thực hiện không phải là rất hiệu quả trong VS2005.

+1

Thats chắc chắn nhất đúng! – AndreasT