2010-02-06 48 views
15

Tôi hơi bối rối về cơ chế của người tạo bản sao. Nếu tôi sai:Tôi có thể gọi một nhà xây dựng bản sao một cách rõ ràng không?

Nếu phương thức lấy tham chiếu đến đối tượng dưới dạng tham số và lớp xác định construtor sao chép, thì lớp sử dụng hàm tạo để tạo bản sao của chính nó và được chuyển đến hàm thay vì tham chiếu đến đối tượng gốc?

Bên cạnh đó, người ta có thể gọi

Object * obj = new Object(&anotherObject); 

để tạo ra một bản sao của anotherObject?

+1

ngữ của bạn không nên đề nghị rằng 'anotherObject' đang tạo ra một bản sao của chính nó .. các đối tượng đó được tạo ra sau khi mới sẽ được tạo ra một bản sao của' anotherObject'. –

+5

@Hassan: Đừng thay đổi ý nghĩa của câu hỏi mà không hỏi OP. Đó là một sự khác biệt quan trọng. '& anotherObject' là một con trỏ, không phải là tham chiếu. –

+0

"người ta không thể nói" rằng đối tượng mới (& anotherObject) 'sẽ tạo ra một bản sao của một đối tượng khác ngầm (đó là những gì từ ngữ được đề xuất). Để tuyên bố đó đúng, cú pháp cần điều chỉnh. –

Trả lời

26

Không, nếu một chức năng phải mất một tài liệu tham khảo:

void f1(Object & o); // call by reference 

thì không có bản sao được thực hiện. Nếu hàm có giá trị:

void f2(Object o); // call by value 

thì bản sao được tạo bởi trình biên dịch bằng cách sử dụng hàm tạo bản sao.

Và vâng, khi bạn nói:

Object * obj = new Object(anotherObject); // not &anotherObject 

constructor sao chép được sử dụng một cách rõ ràng (giả sử anotherObject là kiểu Object.) Không có gì kỳ diệu về việc sử dụng new đây, tuy nhiên là - trong trường hợp này:

Object obj2(anotherObject); 

trình tạo bản sao cũng được sử dụng.

+1

"thì bản sao được tạo bởi trình biên dịch bằng cách sử dụng hàm tạo bản sao." - nó có thể sử dụng move-constructor, và đây cũng là một kịch bản copy-elision vì vậy có lẽ thậm chí không có constructor nào cả. –

7

Nếu phương pháp lấy tham chiếu đến một đối tượng dưới dạng thông số, thì trình tạo bản sao sẽ không được gọi. Nếu đó là trường hợp, sau đó một cuộc gọi đến các nhà xây dựng bản sao chính nó sẽ có kết quả trong một vòng lặp vô hạn (vì nó có một tham chiếu như là một đối số).

Dòng đó không phải là cách hợp lệ để gọi hàm tạo bản sao. Nó mong đợi một tham chiếu như một đối số, không phải là một con trỏ.

+0

Thực ra, đối số copy-constructor không phải là solid. Tiêu chuẩn ngôn ngữ cho phép sao chép tùy chọn trong một số trường hợp và ghi chú rõ ràng rằng việc triển khai phải đảm bảo rằng không có đệ quy vô hạn trong trường hợp copy-constructor, như một ví dụ (xem chú thích 93 trong C++ 03) – AnT

+0

@AndreyT: Thú vị. Dù sao, điều đó không thay đổi mọi thứ trong trường hợp này kể từ khi chúng tôi đang nói về * không * sao chép trong trường hợp tham chiếu, đó là một điều khá chắc chắn. –

+0

Vâng, phần tiêu chuẩn tôi đã đề cập đến thực sự là về khởi tạo tham chiếu và nó cho phép sao chép rõ ràng trong một số trường hợp (trong trường hợp khởi tạo tham chiếu const). Tất nhiên, các trình biên dịch thường không tham gia vào việc sao chép bất hợp pháp hoang dã, nhưng các ví dụ về trình biên dịch tạo ra các bản sao bất hợp pháp (vẫn còn hợp pháp) lạ được biết đến. – AnT

1

Không có trong cả hai trường hợp. Trong trường hợp đầu tiên, tham chiếu tới chính đối tượng đó được truyền và bản sao không được tạo ra. Trong trường hợp thứ hai, bạn chuyển một con trỏ tới hàm tạo của object do đó không có bản sao nào được tạo. Vì vậy, đối tượng nên có một constructor (không phải là một nhà xây dựng bản sao) mà là một cái gì đó giống như object(anotherClass*)

1

Sao chép constructor được gọi là chỉ khi đi qua bởi giá trị, không bằng cách tham khảo. Bằng cách tham chiếu không cần sao chép (đây là một phần của tham chiếu nào là dành cho!) Nên không có hàm tạo bản sao nào được gọi.

3

Thực tế là bạn đang thực hiện cuộc gọi phương thức không quan trọng ở đây. Tham số khởi tạo tham chiếu trong khi gọi hàm không khác với khởi tạo tham chiếu độc lập và được điều chỉnh bởi cùng một quy tắc.

Quy tắc khởi tạo tham chiếu hơi phức tạp một chút, nhưng dòng dưới cùng là nếu trình khởi tạo là một giá trị (đối số trong cuộc gọi phương thức trong trường hợp của bạn) và loại tham chiếu giống như loại initializer (tức là kiểu tham số cũng giống như kiểu đối số), thì tham chiếu sẽ bị ràng buộc trực tiếp. I E. không có bản sao nào được tạo.

Object a; // class type 
Object &r = a; // no copying 
const Object &cr = a; // no copying 

Nếu yêu cầu này không được đáp ứng (ví dụ: nếu trình khởi tạo là giá trị), thì tất cả đều phụ thuộc. Trong một số trường hợp, việc sao chép có thể và sẽ diễn ra. Ví dụ

const Object &tr = Object(); 

có thể được giải thích bởi trình biên dịch như

const Object &tr = Object(Object(Object(Object()))); 

với thực hiện phụ thuộc vào số hữu hạn các copyings. Tất nhiên, vì lý do hiệu quả, các trình biên dịch thường cố gắng không tạo các bản sao không cần thiết, ngay cả khi chúng được phép sao chép.

Một ví dụ điển hình thường khuấy động cuộc tranh luận về tính hợp lệ của các hành vi sao chép các trình biên dịch là khởi tạo tài liệu tham khảo trong các biểu thức như sau một

Object a; 
const Object &r = <some condition> ? a : Object(); 

Một người quen thuộc với ngữ nghĩa C++ tài liệu tham khảo sẽ hiểu rằng biểu thức như ở trên có thể là lý do đằng sau sự cho phép chuẩn để thực hiện sao chép thừa trong quá trình khởi tạo tham chiếu.

+0

bạn có chắc chắn về điều sâu sắc vô hạn đó không? C++ 03 8.5.3 nói rằng chỉ có hai trường hợp: (a) "Tham chiếu được ràng buộc với đối tượng được biểu diễn bởi giá trị (xem 3.10) hoặc cho một đối tượng phụ trong đối tượng đó", và (b) " Một tạm thời kiểu "cv1 T2" [sic] được tạo ra, và một hàm tạo được gọi là sao chép toàn bộ đối tượng rvalue vào tạm thời. Tham chiếu được gắn kết với tạm thời hoặc một đối tượng phụ trong tạm thời ". Lưu ý rằng trường hợp (b) nói * liên kết với *, không * được khởi tạo từ *, vì vậy tôi không thấy cách đó (b) cho phép nhiều hơn một tạm thời thêm. –

+0

Tất nhiên, C++ 14 (xuất hiện sau khi câu trả lời của bạn được viết) chỉ cho phép trường hợp (a), tức là không thể tạo thêm thời gian nào ngoài 'Object()'. Tôi đã không kiểm tra từ ngữ của C++ 11 mặc dù ISTR nó đã có một khiếm khuyết liên quan đến khởi tạo tham chiếu từ rvalues. –

0

có sử dụng vị trí mới như vậy:

Object dstObject; 
new(&dstObject) Object(&anotherObject); 
Các vấn đề liên quan