- Những gì hiện sao chép một đối tượng trung bình?
- Các constructor sao chép và toán tử gán sao chép là gì?
- Khi nào tôi cần phải tự khai báo chúng?
- Làm thế nào tôi có thể ngăn chặn các đối tượng của tôi bị sao chép?
Câu trả lời:
C ++ xử lý các biến của các loại do người dùng định nghĩa với ngữ nghĩa giá trị . Điều này có nghĩa là các đối tượng được sao chép hoàn toàn trong các bối cảnh khác nhau và chúng ta nên hiểu "sao chép một đối tượng" thực sự có nghĩa là gì.
Chúng ta hãy xem xét một ví dụ đơn giản:
class person
{
std::string name;
int age;
public:
person(const std::string& name, int age) : name(name), age(age)
{
}
};
int main()
{
person a("Bjarne Stroustrup", 60);
person b(a); // What happens here?
b = a; // And here?
}
(Nếu bạn bối rối bởi name(name), age(age)
phần này, đây được gọi là danh sách khởi tạo thành viên .)
Sao chép một person
đối tượng có nghĩa là gì? Các main
chức năng cho thấy hai kịch bản sao chép khác nhau. Việc khởi tạo person b(a);
được thực hiện bởi hàm tạo sao chép . Công việc của nó là xây dựng một đối tượng mới dựa trên trạng thái của một đối tượng hiện có. Việc chuyển nhượng b = a
được thực hiện bởi toán tử gán sao chép . Công việc của nó nói chung phức tạp hơn một chút, vì đối tượng mục tiêu đã ở trong một số trạng thái hợp lệ cần được xử lý.
Vì chúng tôi tự khai báo cả hàm tạo sao chép cũng như toán tử gán (cũng không phải hàm hủy), chúng được định nghĩa ngầm cho chúng tôi. Trích dẫn từ tiêu chuẩn:
[...] sao chép hàm tạo và toán tử gán sao chép, [...] và hàm hủy là các hàm thành viên đặc biệt. [ Lưu ý : Việc triển khai sẽ ngầm khai báo các hàm thành viên này cho một số loại lớp khi chương trình không khai báo rõ ràng chúng. Việc thực hiện sẽ ngầm định nghĩa chúng nếu chúng được sử dụng. [...] ghi chú cuối ] [n3126.pdf phần 12 §1]
Theo mặc định, sao chép một đối tượng có nghĩa là sao chép các thành viên của nó:
Hàm tạo sao chép được định nghĩa ngầm định cho lớp không liên kết X thực hiện một bản sao thành viên của các tiểu dự án của nó. [n3126.pdf phần 12.8 §16]
Toán tử gán sao chép được định nghĩa ngầm định cho lớp không liên kết X thực hiện gán sao chép thành viên của các tiểu dự án của nó. [n3126.pdf phần 12.8 §30]
Các hàm thành viên đặc biệt được định nghĩa ngầm định person
trông như thế này:
// 1. copy constructor
person(const person& that) : name(that.name), age(that.age)
{
}
// 2. copy assignment operator
person& operator=(const person& that)
{
name = that.name;
age = that.age;
return *this;
}
// 3. destructor
~person()
{
}
Sao chép thành viên là chính xác những gì chúng ta muốn trong trường hợp này:
name
và age
được sao chép, vì vậy chúng ta có được một person
đối tượng độc lập, độc lập . Hàm hủy được định nghĩa ngầm luôn trống. Điều này cũng tốt trong trường hợp này vì chúng tôi không có được bất kỳ tài nguyên nào trong hàm tạo. Các hàm hủy của các thành viên được gọi ngầm sau khi hàm person
hủy kết thúc:
Sau khi thực thi phần thân của hàm hủy và phá hủy bất kỳ đối tượng tự động nào được phân bổ trong phần thân, một hàm hủy cho lớp X gọi các hàm hủy cho các thành viên [...] trực tiếp của X [n3126.pdf 12.4 §6]
Vậy khi nào chúng ta nên khai báo các hàm thành viên đặc biệt một cách rõ ràng? Khi lớp của chúng ta quản lý một tài nguyên , nghĩa là khi một đối tượng của lớp chịu trách nhiệm về tài nguyên đó. Điều đó thường có nghĩa là tài nguyên được thu nhận trong hàm tạo (hoặc được truyền vào hàm tạo) và được giải phóng trong hàm hủy.
Chúng ta hãy quay ngược thời gian để chuẩn C ++. Không có điều gì như vậy std::string
, và các lập trình viên đã yêu con trỏ. Các person
lớp có thể nhìn như thế này:
class person
{
char* name;
int age;
public:
// the constructor acquires a resource:
// in this case, dynamic memory obtained via new[]
person(const char* the_name, int the_age)
{
name = new char[strlen(the_name) + 1];
strcpy(name, the_name);
age = the_age;
}
// the destructor must release this resource via delete[]
~person()
{
delete[] name;
}
};
Ngay cả ngày nay, mọi người vẫn viết các lớp theo phong cách này và gặp rắc rối: " Tôi đã đẩy một người vào một vectơ và bây giờ tôi gặp lỗi bộ nhớ điên rồ! " Hãy nhớ rằng theo mặc định, sao chép một đối tượng có nghĩa là sao chép các thành viên của nó, nhưng sao chépname
thành viên sao chép một con trỏ, không phải mảng ký tự mà nó trỏ tới! Điều này có một số tác dụng khó chịu:
a
có thể được quan sát thông qua b
.b
bị phá hủy, a.name
là một con trỏ lơ lửng.a
bị phá hủy, xóa con trỏ lơ lửng sẽ mang lại hành vi không xác định .name
chỉ ra trước khi chuyển nhượng, sớm hay muộn bạn sẽ bị rò rỉ bộ nhớ ở mọi nơi.Vì sao chép thành viên không có hiệu ứng mong muốn, chúng ta phải xác định rõ ràng hàm tạo sao chép và toán tử gán gán sao chép để tạo các bản sao sâu của mảng ký tự:
// 1. copy constructor
person(const person& that)
{
name = new char[strlen(that.name) + 1];
strcpy(name, that.name);
age = that.age;
}
// 2. copy assignment operator
person& operator=(const person& that)
{
if (this != &that)
{
delete[] name;
// This is a dangerous point in the flow of execution!
// We have temporarily invalidated the class invariants,
// and the next statement might throw an exception,
// leaving the object in an invalid state :(
name = new char[strlen(that.name) + 1];
strcpy(name, that.name);
age = that.age;
}
return *this;
}
Lưu ý sự khác biệt giữa khởi tạo và gán: chúng ta phải phá bỏ trạng thái cũ trước khi gán name
để tránh rò rỉ bộ nhớ. Ngoài ra, chúng tôi phải bảo vệ chống lại việc tự gán mẫu x = x
. Nếu không có kiểm tra đó, delete[] name
sẽ xóa mảng chứa chuỗi nguồn , bởi vì khi bạn viết x = x
, cả hai this->name
và that.name
chứa cùng một con trỏ.
Thật không may, giải pháp này sẽ thất bại nếu new char[...]
ném ngoại lệ do cạn kiệt bộ nhớ. Một giải pháp có thể là giới thiệu một biến cục bộ và sắp xếp lại các câu lệnh:
// 2. copy assignment operator
person& operator=(const person& that)
{
char* local_name = new char[strlen(that.name) + 1];
// If the above statement throws,
// the object is still in the same state as before.
// None of the following statements will throw an exception :)
strcpy(local_name, that.name);
delete[] name;
name = local_name;
age = that.age;
return *this;
}
Điều này cũng quan tâm đến việc tự giao mà không cần kiểm tra rõ ràng. Một giải pháp mạnh mẽ hơn nữa cho vấn đề này là thành ngữ sao chép và trao đổi , nhưng tôi sẽ không đi sâu vào chi tiết về an toàn ngoại lệ ở đây. Tôi chỉ đề cập đến các ngoại lệ để đưa ra quan điểm sau: Viết các lớp quản lý tài nguyên là khó.
Một số tài nguyên không thể hoặc không được sao chép, chẳng hạn như xử lý tệp hoặc mutexes. Trong trường hợp đó, chỉ cần khai báo hàm tạo sao chép và toán tử gán gán sao chép private
mà không đưa ra định nghĩa:
private:
person(const person& that);
person& operator=(const person& that);
Ngoài ra, bạn có thể kế thừa từ boost::noncopyable
hoặc khai báo chúng là đã xóa (trong C ++ 11 trở lên):
person(const person& that) = delete;
person& operator=(const person& that) = delete;
Đôi khi bạn cần triển khai một lớp quản lý tài nguyên. (Không bao giờ quản lý nhiều tài nguyên trong một lớp, điều này sẽ chỉ dẫn đến đau.) Trong trường hợp đó, hãy nhớ quy tắc ba :
Nếu bạn cần phải khai báo rõ ràng hàm hủy, sao chép hàm tạo hoặc toán tử gán gán sao chép, có lẽ bạn cần khai báo rõ ràng cả ba hàm này.
(Thật không may, "quy tắc" này không được thi hành theo tiêu chuẩn C ++ hoặc bất kỳ trình biên dịch nào tôi biết.)
Từ C ++ 11 trở đi, một đối tượng có thêm 2 hàm thành viên đặc biệt: hàm tạo di chuyển và phép gán di chuyển. Quy tắc của năm tiểu bang để thực hiện các chức năng là tốt.
Một ví dụ với chữ ký:
class person
{
std::string name;
int age;
public:
person(const std::string& name, int age); // Ctor
person(const person &) = default; // Copy Ctor
person(person &&) noexcept = default; // Move Ctor
person& operator=(const person &) = default; // Copy Assignment
person& operator=(person &&) noexcept = default; // Move Assignment
~person() noexcept = default; // Dtor
};
Quy tắc 3/5 cũng được gọi là quy tắc 0/3/5. Phần không của quy tắc nói rằng bạn được phép không viết bất kỳ hàm thành viên đặc biệt nào khi tạo lớp của bạn.
Hầu hết thời gian, bạn không cần phải tự mình quản lý tài nguyên, bởi vì một lớp hiện có như std::string
đã làm điều đó cho bạn. Chỉ cần so sánh mã đơn giản bằng cách sử dụng một std::string
thành viên với giải pháp thay thế dễ bị lỗi và dễ bị lỗi bằng cách sử dụng a char*
và bạn sẽ bị thuyết phục. Miễn là bạn tránh xa các thành viên con trỏ thô, quy tắc ba không có khả năng liên quan đến mã của riêng bạn.
Các Rule của Ba là một quy luật của cho C ++, về cơ bản nói
Nếu lớp của bạn cần bất kỳ
- một nhà xây dựng sao chép ,
- một toán tử gán ,
- hoặc một kẻ hủy diệt ,
được định nghĩa rõ ràng, sau đó có khả năng cần cả ba người trong số họ .
Lý do cho điều này là cả ba trong số chúng thường được sử dụng để quản lý tài nguyên và nếu lớp của bạn quản lý tài nguyên, nó thường cần quản lý sao chép cũng như giải phóng.
Nếu không có ngữ nghĩa tốt để sao chép tài nguyên mà lớp của bạn quản lý, thì hãy xem xét cấm sao chép bằng cách khai báo (không xác định ) hàm tạo sao chép và toán tử gán như private
.
(Lưu ý rằng phiên bản mới sắp tới của tiêu chuẩn C ++ (là C ++ 11) thêm ngữ nghĩa di chuyển vào C ++, có khả năng sẽ thay đổi Quy tắc ba. Tuy nhiên, tôi biết quá ít về điều này để viết phần C ++ 11 về quy tắc của ba.)
boost::noncopyable
). Nó cũng có thể rõ ràng hơn nhiều. Tôi nghĩ rằng C ++ 0x và khả năng "xóa" các chức năng có thể giúp ích ở đây, nhưng đã quên cú pháp: /
noncopyable
là một phần của lib std, tôi không coi đó là một cải tiến. (Ồ, và nếu bạn quên cú pháp xóa, bạn đã quên mor ethan tôi từng biết. :)
)
Luật của ba lớn là như quy định ở trên.
Một ví dụ dễ hiểu, bằng tiếng Anh đơn giản, về loại vấn đề mà nó giải quyết:
Hàm hủy không mặc định
Bạn đã phân bổ bộ nhớ trong hàm tạo của bạn và do đó bạn cần phải viết một hàm hủy để xóa nó. Nếu không, bạn sẽ gây rò rỉ bộ nhớ.
Bạn có thể nghĩ rằng đây là công việc được thực hiện.
Vấn đề sẽ là, nếu một bản sao được tạo từ đối tượng của bạn, thì bản sao đó sẽ trỏ đến cùng bộ nhớ với đối tượng ban đầu.
Một khi, một trong số này xóa bộ nhớ trong hàm hủy của nó, cái còn lại sẽ có một con trỏ tới bộ nhớ không hợp lệ (cái này được gọi là con trỏ lơ lửng) khi nó cố gắng sử dụng nó, mọi thứ sẽ có lông.
Do đó, bạn viết một hàm tạo sao chép để nó phân bổ các đối tượng mới các phần bộ nhớ của riêng chúng để hủy.
Toán tử gán và xây dựng bản sao
Bạn đã phân bổ bộ nhớ trong hàm tạo của bạn cho một con trỏ thành viên của lớp. Khi bạn sao chép một đối tượng của lớp này, toán tử gán mặc định và hàm tạo sao chép sẽ sao chép giá trị của con trỏ thành viên này sang đối tượng mới.
Điều này có nghĩa là đối tượng mới và đối tượng cũ sẽ chỉ vào cùng một phần bộ nhớ, vì vậy khi bạn thay đổi nó trong một đối tượng, nó cũng sẽ bị thay đổi đối với các objerct khác. Nếu một đối tượng xóa bộ nhớ này, đối tượng khác sẽ tiếp tục cố gắng sử dụng nó - eek.
Để giải quyết vấn đề này, bạn viết phiên bản riêng của hàm tạo sao chép và toán tử gán. Các phiên bản của bạn phân bổ bộ nhớ riêng cho các đối tượng mới và sao chép qua các giá trị mà con trỏ đầu tiên đang trỏ đến thay vì địa chỉ của nó.
Về cơ bản nếu bạn có một hàm hủy (không phải là hàm hủy mặc định) thì có nghĩa là lớp mà bạn đã xác định có một số cấp phát bộ nhớ. Giả sử rằng lớp được sử dụng bên ngoài bởi một số mã máy khách hoặc bởi bạn.
MyClass x(a, b);
MyClass y(c, d);
x = y; // This is a shallow copy if assignment operator is not provided
Nếu MyClass chỉ có một số thành viên được nhập nguyên thủy, toán tử gán mặc định sẽ hoạt động nhưng nếu nó có một số thành viên con trỏ và các đối tượng không có toán tử gán thì kết quả sẽ không thể đoán trước. Do đó, chúng ta có thể nói rằng nếu có một cái gì đó cần xóa trong hàm hủy của một lớp, chúng ta có thể cần một toán tử sao chép sâu, điều đó có nghĩa là chúng ta nên cung cấp một hàm tạo sao chép và toán tử gán.
Sao chép một đối tượng có nghĩa là gì? Có một số cách bạn có thể sao chép các đối tượng - hãy nói về 2 loại mà bạn có thể đề cập nhất - bản sao sâu và bản sao nông.
Vì chúng tôi đang sử dụng ngôn ngữ hướng đối tượng (hoặc ít nhất là giả định như vậy), giả sử bạn có một phần bộ nhớ được phân bổ. Vì đó là ngôn ngữ OO, chúng ta có thể dễ dàng tham khảo các khối bộ nhớ mà chúng ta phân bổ vì chúng thường là các biến nguyên thủy (ints, chars, byte) hoặc các lớp mà chúng ta đã xác định được tạo từ các kiểu và nguyên thủy của chính chúng ta. Vì vậy, hãy nói rằng chúng tôi có một lớp Xe như sau:
class Car //A very simple class just to demonstrate what these definitions mean.
//It's pseudocode C++/Javaish, I assume strings do not need to be allocated.
{
private String sPrintColor;
private String sModel;
private String sMake;
public changePaint(String newColor)
{
this.sPrintColor = newColor;
}
public Car(String model, String make, String color) //Constructor
{
this.sPrintColor = color;
this.sModel = model;
this.sMake = make;
}
public ~Car() //Destructor
{
//Because we did not create any custom types, we aren't adding more code.
//Anytime your object goes out of scope / program collects garbage / etc. this guy gets called + all other related destructors.
//Since we did not use anything but strings, we have nothing additional to handle.
//The assumption is being made that the 3 strings will be handled by string's destructor and that it is being called automatically--if this were not the case you would need to do it here.
}
public Car(const Car &other) // Copy Constructor
{
this.sPrintColor = other.sPrintColor;
this.sModel = other.sModel;
this.sMake = other.sMake;
}
public Car &operator =(const Car &other) // Assignment Operator
{
if(this != &other)
{
this.sPrintColor = other.sPrintColor;
this.sModel = other.sModel;
this.sMake = other.sMake;
}
return *this;
}
}
Một bản sao sâu là nếu chúng ta khai báo một đối tượng và sau đó tạo một bản sao hoàn toàn riêng biệt của đối tượng ... chúng ta kết thúc với 2 đối tượng trong 2 bộ nhớ hoàn toàn.
Car car1 = new Car("mustang", "ford", "red");
Car car2 = car1; //Call the copy constructor
car2.changePaint("green");
//car2 is now green but car1 is still red.
Bây giờ hãy làm điều gì đó kỳ lạ. Giả sử car2 được lập trình sai hoặc cố ý chia sẻ bộ nhớ thực mà car1 được tạo ra. (Thường thì đó là một sai lầm khi làm điều này và trong các lớp học thường là cái chăn được thảo luận.) Giả vờ rằng bất cứ khi nào bạn hỏi về car2, bạn thực sự đang giải quyết một con trỏ đến không gian bộ nhớ của car1 ... đó là một bản sao nông Là.
//Shallow copy example
//Assume we're in C++ because it's standard behavior is to shallow copy objects if you do not have a constructor written for an operation.
//Now let's assume I do not have any code for the assignment or copy operations like I do above...with those now gone, C++ will use the default.
Car car1 = new Car("ford", "mustang", "red");
Car car2 = car1;
car2.changePaint("green");//car1 is also now green
delete car2;/*I get rid of my car which is also really your car...I told C++ to resolve
the address of where car2 exists and delete the memory...which is also
the memory associated with your car.*/
car1.changePaint("red");/*program will likely crash because this area is
no longer allocated to the program.*/
Vì vậy, bất kể bạn đang viết ngôn ngữ nào, hãy cẩn thận về ý nghĩa của bạn khi sao chép các đối tượng vì hầu hết thời gian bạn muốn có một bản sao sâu.
Các constructor sao chép và toán tử gán sao chép là gì? Tôi đã sử dụng chúng ở trên. Hàm tạo sao chép được gọi khi bạn nhập mã như Car car2 = car1;
Về cơ bản nếu bạn khai báo một biến và gán nó trong một dòng, đó là khi hàm tạo sao chép được gọi. Toán tử gán là những gì xảy ra khi bạn sử dụng dấu bằng-- car2 = car1;
. Thông báo car2
không được tuyên bố trong cùng một tuyên bố. Hai đoạn mã bạn viết cho các hoạt động này có thể rất giống nhau. Trong thực tế, mẫu thiết kế điển hình có một chức năng khác mà bạn gọi để đặt mọi thứ một khi bạn hài lòng, bản sao / bài tập ban đầu là hợp pháp - nếu bạn nhìn vào mã dài tay tôi đã viết, các hàm này gần giống nhau.
Khi nào tôi cần phải tự khai báo chúng? Nếu bạn không viết mã được chia sẻ hoặc sản xuất theo cách nào đó, bạn thực sự chỉ cần khai báo chúng khi bạn cần chúng. Bạn cần phải biết ngôn ngữ chương trình của bạn làm gì nếu bạn chọn sử dụng nó 'một cách tình cờ' và không tạo ra một ngôn ngữ - tức là bạn có được trình biên dịch mặc định. Tôi hiếm khi sử dụng các hàm tạo sao chép, nhưng ghi đè toán tử gán là rất phổ biến. Bạn có biết bạn có thể ghi đè cả phép cộng, phép trừ, v.v.
Làm thế nào tôi có thể ngăn chặn các đối tượng của tôi bị sao chép? Ghi đè tất cả các cách bạn được phép phân bổ bộ nhớ cho đối tượng của mình bằng chức năng riêng là một sự khởi đầu hợp lý. Nếu bạn thực sự không muốn mọi người sao chép chúng, bạn có thể công khai và cảnh báo cho lập trình viên bằng cách ném một ngoại lệ và cũng không sao chép đối tượng.
Khi nào tôi cần phải tự khai báo chúng?
Quy tắc của ba quy định rằng nếu bạn tuyên bố bất kỳ
sau đó bạn nên khai báo cả ba. Nó phát triển từ quan sát rằng sự cần thiết phải tiếp nhận ý nghĩa của hoạt động sao chép hầu như luôn xuất phát từ lớp thực hiện một số loại quản lý tài nguyên và hầu như luôn ngụ ý rằng
bất cứ việc quản lý tài nguyên nào đã được thực hiện trong một thao tác sao chép có thể cần phải được thực hiện trong hoạt động sao chép khác và
hàm hủy lớp cũng sẽ tham gia quản lý tài nguyên (thường phát hành nó). Tài nguyên cổ điển được quản lý là bộ nhớ và đây là lý do tại sao tất cả các lớp Thư viện tiêu chuẩn quản lý bộ nhớ (ví dụ: các bộ chứa STL thực hiện quản lý bộ nhớ động) đều khai báo ba bộ ba lớn: cả hoạt động sao chép và bộ hủy.
Hậu quả của Quy tắc ba là sự hiện diện của hàm hủy do người dùng khai báo cho thấy rằng bản sao thông minh thành viên đơn giản khó có thể phù hợp với các hoạt động sao chép trong lớp. Điều đó, đến lượt nó, gợi ý rằng nếu một lớp tuyên bố một hàm hủy, các hoạt động sao chép có lẽ không nên được tạo tự động, bởi vì chúng sẽ không làm đúng. Vào thời điểm C ++ 98 được thông qua, tầm quan trọng của dòng lý luận này chưa được đánh giá đầy đủ, vì vậy, trong C ++ 98, sự tồn tại của một người sử dụng tuyên bố hủy diệt không ảnh hưởng đến sự sẵn sàng của trình biên dịch để tạo ra các hoạt động sao chép. Điều đó tiếp tục là trường hợp trong C ++ 11, nhưng chỉ vì việc hạn chế các điều kiện mà các hoạt động sao chép được tạo ra sẽ phá vỡ quá nhiều mã kế thừa.
Làm thế nào tôi có thể ngăn chặn các đối tượng của tôi bị sao chép?
Khai báo hàm tạo sao chép & toán tử gán sao chép dưới dạng chỉ định truy cập riêng.
class MemoryBlock
{
public:
//code here
private:
MemoryBlock(const MemoryBlock& other)
{
cout<<"copy constructor"<<endl;
}
// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other)
{
return *this;
}
};
int main()
{
MemoryBlock a;
MemoryBlock b(a);
}
Trong C ++ 11 trở đi, bạn cũng có thể khai báo toán tử sao chép & toán tử gán
class MemoryBlock
{
public:
MemoryBlock(const MemoryBlock& other) = delete
// Copy assignment operator.
MemoryBlock& operator=(const MemoryBlock& other) =delete
};
int main()
{
MemoryBlock a;
MemoryBlock b(a);
}
Nhiều câu trả lời hiện có đã chạm vào hàm tạo sao chép, toán tử gán và hàm hủy. Tuy nhiên, trong bài C ++ 11, việc giới thiệu di chuyển ngữ nghĩa có thể mở rộng điều này vượt quá 3.
Gần đây Michael Claisse đã có một bài nói chuyện chạm vào chủ đề này: http://channel9.msdn.com/events/CPP/C-PP-Con-2014/The-Canonical-Class
Quy tắc ba trong C ++ là một nguyên tắc cơ bản của thiết kế và phát triển ba yêu cầu mà nếu có định nghĩa rõ ràng trong một trong các hàm thành viên sau, thì lập trình viên nên xác định hai hàm thành viên còn lại với nhau. Cụ thể ba chức năng thành viên sau đây là không thể thiếu: hàm hủy, hàm tạo sao chép, toán tử gán sao chép.
Copy constructor trong C ++ là một constructor đặc biệt. Nó được sử dụng để xây dựng một đối tượng mới, đó là đối tượng mới tương đương với một bản sao của một đối tượng hiện có.
Toán tử gán gán là một toán tử gán đặc biệt thường được sử dụng để chỉ định một đối tượng hiện có cho các đối tượng khác có cùng loại đối tượng.
Có những ví dụ nhanh:
// default constructor
My_Class a;
// copy constructor
My_Class b(a);
// copy constructor
My_Class c = a;
// copy assignment operator
b = a;
c++-faq
thẻ wiki trước khi bạn bỏ phiếu đóng cửa .