2011-01-15 2 views
13

Sau khi đọc về các nhà xây dựng sao chép và sao chép các toán tử gán trong C++, tôi đã cố tạo một ví dụ đơn giản. Mặc dù đoạn mã bên dưới dường như hoạt động, tôi không chắc liệu tôi có đang triển khai bản sao chép và sao chép toán tử gán đúng cách hay không. Bạn có thể vui lòng chỉ ra nếu có bất kỳ sai sót/cải tiến hoặc một ví dụ tốt hơn để hiểu các khái niệm có liên quan.C++: Triển khai thực hiện sao chép bản sao và sao chép phép gán

class Foobase 
{ 
    int bInt; 

public: 
    Foobase() {} 

    Foobase(int b) { bInt = b;} 

    int GetValue() { return bInt;} 

    int SetValue(const int& val) { bInt = val; } 
}; 


class Foobar 
{ 
    int var;  
    Foobase *base;  

public: 
    Foobar(){} 

    Foobar(int v) 
    { 
     var = v;   
     base = new Foobase(v * -1); 

    } 

    //Copy constructor 
    Foobar(const Foobar& foo) 
    {  
     var = foo.var; 
     base = new Foobase(foo.GetBaseValue()); 
    } 

    //Copy assignemnt operator 
    Foobar& operator= (const Foobar& other) 
    { 
     if (this != &other) // prevent self-assignment 
     { 
      var = other.var; 
      base = new Foobase(other.GetBaseValue()); 

     } 
     return *this; 
    } 

    ~Foobar() 
    { 
     delete base; 
    } 

    void SetValue(int val) 
    { 
     var = val; 
    } 

    void SetBaseValue(const int& val) 
    { 
     base->SetValue(val); 
    } 

    int GetBaseValue() const 
    { 
     return(base->GetValue()); 
    } 

    void Print() 
    { 
     cout<<"Foobar Value: "<<var<<endl; 
     cout<<"Foobase Value: "<<base->GetValue()<<endl; 

    } 

}; 

int main() 
{ 
    Foobar f(10);  
    Foobar g(f); //calls copy constructor 
    Foobar h = f; //calls copy constructor 

    Foobar i; 
    i = f; 

    f.SetBaseValue(12); 
    f.SetValue(2);  

    Foobar j = f = z; //copy constructor for j but assignment operator for f 

    z.SetBaseValue(777); 
    z.SetValue(77); 

    return 1; 
} 

Trả lời

12

Toán tử gán bản sao của bạn được triển khai không chính xác. Đối tượng được chỉ định để rò rỉ đối tượng của nó là base điểm.

Nhà xây dựng mặc định của bạn cũng không chính xác: nó để lại cả basevar chưa được khởi tạo, vì vậy không có cách nào để biết liệu có hợp lệ hay không trong destructor khi bạn gọi delete base;.

Cách dễ nhất để triển khai trình tạo bản sao và sao chép toán tử gán và biết rằng bạn đã làm đúng cách là sử dụng the Copy-and-Swap idiom.

+1

+1 điểm tốt sao chép và hoán đổi thành ngữ là điều cần thiết để có một thực thi ngoại lệ an toàn đảm bảo đối tượng vẫn ở trạng thái nhất quán. – jdehaan

+0

Sẽ không bộ nhớ được giải phóng bởi destructor '~ Foobar()'? – blitzkriegz

+0

@Mahatma: '~ Foobar()' sẽ không được gọi; đối tượng không bao giờ bị hủy, nó được gán cho. –

2

Chỉ Foobar cần một hàm tạo bản sao tùy chỉnh, toán tử gán và hàm hủy. Foobase không cần một vì hành vi mặc định trình biên dịch cho là đủ tốt.

Trong trường hợp Foobar bạn bị rò rỉ trong toán tử gán. Bạn có thể dễ dàng sửa chữa nó bằng cách giải phóng các đối tượng trước khi phân bổ nó, và đó là đủ tốt. Nhưng nếu bạn đã thêm thành viên con trỏ thứ hai thành Foobar, bạn sẽ thấy rằng khi mọi thứ trở nên phức tạp. Bây giờ, nếu bạn có một ngoại lệ trong khi phân bổ con trỏ thứ hai, bạn cần phải dọn dẹp đúng con trỏ đầu tiên bạn đã phân bổ, để tránh tham nhũng hoặc rò rỉ. Và mọi thứ trở nên phức tạp hơn nhiều so với cách thức đa thức khi bạn thêm nhiều thành viên trỏ chuột.

Thay vào đó, những gì bạn muốn làm là triển khai toán tử gán về mặt hàm tạo bản sao. Sau đó, bạn nên triển khai hàm tạo bản sao theo một hàm hoán đổi không ném. Đọc về thành phần Sao chép & Hoán đổi để biết chi tiết.

Ngoài ra, hàm tạo mặc định của Foobar không mặc định khởi tạo thành viên. Đó là xấu, bởi vì nó không phải là những gì người dùng mong đợi. Con trỏ thành viên trỏ vào một địa chỉ tùy ý và int có một giá trị tùy ý. Bây giờ nếu bạn sử dụng đối tượng mà hàm tạo đã tạo, bạn ở rất gần Vùng hành vi không xác định.

+0

Nếu tôi thực hiện 'xóa cơ sở;' trước khi phân bổ mới để giải phóng bộ nhớ gốc, tôi gặp lỗi 'double free or corruption'. Tôi sẽ nhìn vào thành ngữ. Tôi đã không nhận được phần cuối cùng về vấn đề với constructor mặc định rỗng. Tôi có nên đặt 'var = SOMEVALUE;' và 'base = NULL;' vì trong ví dụ của tôi, tôi sử dụng hàm tạo tham số. – blitzkriegz

+0

Sự vắng mặt của dấu ngoặc đơn trong * biểu thức mới * chỉ tạo ra sự khác biệt cho các kiểu không có hàm tạo, mà không có kiểu nào ở đây. * default-initialization * của các kiểu với ít nhất một constructor do người dùng định nghĩa tạo ra một lời gọi tới constructor mặc định. –

+0

Về mặt đạo đức, hàm tạo mặc định KHÔNG * khởi tạo mặc định * thành viên (không có * ctor-initializer *, chúng không được đề cập trong bất kỳ * mem-initializer-id * nào và tiêu chuẩn xác định rằng chúng là * được khởi tạo mặc định * trong trường hợp này). Nhưng khởi tạo mặc định của một 'int' rời khỏi trạng thái không xác định. –

0

Tôi có một miếng vá rất đơn giản dành cho bạn:

class Foobar 
{ 
    int var;  
    std::unique_ptr<FooBase> base; 

... 

Điều đó sẽ giúp bạn bắt đầu.

Điểm mấu chốt là:

  1. Đừng gọi delete trong mã của bạn (Các chuyên gia thấy điểm 2)
  2. Đừng gọi delete trong mã của bạn (bạn biết rõ hơn ...)
+0

Tùy thuộc vào độ tuổi của thư viện C++ được cung cấp cùng với trình biên dịch, nó có thể là 'std :: tr1 :: unique_ptr' ... –