2009-11-18 32 views
10

Dưới đây là đoạn mã nhỏ:Tại sao copy constructor không được gọi trong trường hợp này?

class A 
{ 
public: 
    A(int value) : value_(value) 
    { 
     cout <<"Regular constructor" <<endl; 
    } 

    A(const A& other) : value_(other.value_) 
    { 
     cout <<"Copy constructor" <<endl; 
    } 

private: 
    int value_; 
}; 
int main() 
{ 
    A a = A(5); 
} 

tôi cho rằng đầu ra sẽ là "Regular Constructor" (ví RHS) tiếp theo là "Copy constructor" cho LHS. Vì vậy, tôi tránh phong cách này và luôn luôn tuyên bố biến của lớp là A a(5);. Nhưng với sự ngạc nhiên của tôi trong đoạn mã trên, hàm dựng không bao giờ được gọi (Visual C++ 2008)

Không ai biết hành vi này là kết quả của tối ưu hóa trình biên dịch hay một số tính năng (và di động) được ghi chép của C++? Cảm ơn.

+0

nó được tối ưu hóa, tránh tạo + sao chép. Tôi thấy rằng đó là một giả định tốt rằng không có người dùng nào sẽ xây dựng từ các tham số, khác với những gì nó được thực hiện sao chép-xây dựng – jpinto3912

+0

Xem thêm: http: // stackoverflow.com/questions/1394229/compreh-return-value-optimization-và-return-temporaries-c –

+1

Trong g ++, bạn có thể vô hiệu hóa tối ưu hóa này với tùy chọn -fide-elide-constructors – Fred

Trả lời

13

Từ bình luận khác: "Vì vậy, theo mặc định tôi không nên dựa vào nó (vì nó có thể phụ thuộc vào trình biên dịch)"

Không, nó không phụ thuộc vào trình biên dịch, thực tế anyway. Bất kỳ trình biên dịch có giá trị một hạt cát sẽ không lãng phí thời gian xây dựng một A, sau đó sao chép nó hơn.

Trong tiêu chuẩn, nó nói rõ ràng rằng nó hoàn toàn có thể chấp nhận được đối với T = x; tương đương với số T(x);. (§12.8.15, trang 211) Thực hiện điều này với T(T(x)) rõ ràng là không cần thiết, do đó, nó sẽ xóa bên trong T.

Để có được hành vi mong muốn, bạn sẽ buộc các trình biên dịch để mặc định xây dựng A đầu tiên:

A a; 
// A is now a fully constructed object, 
// so it can't call constructors again: 
a = A(5); 
+0

Cảm ơn GMan, tôi chưa bao giờ sử dụng cú pháp này. Chỉ cần một cái gì đó để nhớ. Oh, chỉ tìm thấy trên msdn: C++ tiêu chuẩn cho phép các elision của các nhà xây dựng bản sao (xem phần 12.8. Sao chép các đối tượng lớp, đoạn 15) – BostonLogan

+0

Cảm ơn, tôi nhìn thấy nó ngay bây giờ. Đó là một chút để trích dẫn trong một câu trả lời, vì vậy tôi sẽ chỉ tham khảo nó theo số. – GManNickG

+1

Nó phụ thuộc vào trình biên dịch. Tiêu chuẩn cho phép hành vi khác nhau, xem câu trả lời của tôi. –

4

Ở đây bạn có sao chép khởi của a từ tạm A(5). Thực hiện cho phép bỏ qua hàm tạo bản sao gọi điện tại đây theo C++ Standard 12.2/2.

+0

Tôi không nghĩ điều đó đúng. Ví dụ trong 12.2.2 liên quan đến việc tạm thời được truyền cho một hàm trước khi xây dựng đối tượng cục bộ. –

-1
A a = A(5); 

dòng này tương đương với

A a(5); 

Mặc dù xuất hiện chức năng theo phong cách của mình, dòng đầu tiên chỉ đơn giản là xây dựng a với đối số 5. ​​Không sao chép hoặc là tạm thời có liên quan. Từ tiêu chuẩn C++, mục 12.1.11:

Chuyển đổi loại ký hiệu chức năng (5.2.3) có thể được sử dụng để tạo đối tượng mới thuộc loại của nó. [Lưu ý: Cú pháp trông giống như một cuộc gọi rõ ràng của hàm tạo. —end note]

+0

Không, nó không tương đương. Một là * khởi tạo bản sao *, cái kia là * khởi tạo trực tiếp *. –

4

Tôi đã nghiên cứu câu trả lời này để trả lời câu hỏi khác đã bị đóng, vì vậy để không làm cho công việc bị lãng phí, tôi sẽ trả lời câu hỏi này.

Tuyên bố có dạng A a = A(5) được gọi là sao chép khởi tạo của biến a. Tiêu chuẩn C++ 11, 8.5/16 tiểu bang:

Chức năng được chọn được gọi với biểu thức khởi tạo là đối số; nếu hàm là một hàm khởi tạo, cuộc gọi sẽ khởi tạo một phiên bản tạm thời của loại đích không có đủ tiêu chuẩn của một loại . Các tạm thời là một giá trị.Kết quả của cuộc gọi (đó là tạm thời cho trường hợp hàm tạo) sau đó được sử dụng để khởi tạo trực tiếp, theo đến các quy tắc trên, đối tượng là đích đến của việc khởi tạo sao chép . Trong một số trường hợp, việc triển khai được phép để loại bỏ việc sao chép vốn có trong quá trình khởi tạo trực tiếp này bằng cách xây dựng kết quả trung gian trực tiếp vào đối tượng đang được khởi tạo ; xem 12.2, 12.8.

Điều này có nghĩa là trình biên dịch tra cứu hàm tạo thích hợp để xử lý A(5), tạo tạm thời và bản sao tạm thời đó thành a. Nhưng trong hoàn cảnh nào thì bản sao có thể bị loại bỏ?

Hãy xem những gì 12,8/31 nói:

Khi tiêu chí nhất định được đáp ứng, một thực hiện được phép bỏ qua xây dựng sao chép/di chuyển của một đối tượng lớp, ngay cả khi sao chép/di chuyển constructor và/hoặc destructor cho đối tượng có tác dụng phụ. Trong trường hợp này, việc triển khai xử lý nguồn và mục tiêu của hoạt động sao chép/di chuyển bị bỏ qua chỉ đơn giản là hai cách khác nhau để giới thiệu cho cùng một đối tượng và sự hủy diệt đối tượng đó xảy ra tại sau đó khi hai đối tượng sẽ bị phá hủy mà không cần tối ưu hóa. sự bỏ bớt này hoạt động sao chép/di chuyển, gọi bản sao sự bỏ bớt, được cho phép trong các trường hợp sau (có thể được kết hợp để loại bỏ nhiều bản sao):

[...]

  • khi một đối tượng lớp tạm thời chưa được liên kết với tham chiếu (12.2) sẽ được sao chép/di chuyển đến một đối tượng lớp có cùng loại cv-unqualified, hoạt động sao chép/di chuyển có thể bị bỏ qua bằng cách xây dựng đối tượng tạm thời trực tiếp vào mục tiêu của bản sao/di chuyển bị bỏ qua

Có tất cả điều này trong tâm trí, đây là những gì sẽ xảy ra với các biểu hiện A a = A(5):

  1. Trình biên dịch thấy một tuyên bố với bản sao-khởi
  2. Các constructor A(int) được chọn để khởi tạo một đối tượng tạm thời
  3. Vì đối tượng tạm thời là không phải được ràng buộc với tham chiếu và nó có cùng loại A làm loại stination trong biểu thức khởi tạo sao chép, trình biên dịch được phép trực tiếp xây dựng một đối tượng thành a, bỏ qua tạm thời
Các vấn đề liên quan