2009-02-25 41 views
12

Câu hỏi đơn giản: các câu sau đây có tương đương không? hoặc là người thứ hai làm nhiều điều ngầm hơn đằng sau hậu trường (nếu có, cái gì?)Cú pháp hàm tạo C++

myClass x(3); 
myClass x = myClass(3); 

Cảm ơn!

Trả lời

25

Chúng không hoàn toàn giống nhau. Việc đầu tiên được gọi là "khởi tạo trực tiếp" trong khi thứ hai được gọi là "khởi tạo bản sao".

Hiện tại, Chuẩn tạo nên hai quy tắc. Đầu tiên là khởi tạo trực tiếp và khởi tạo bản sao nơi bộ khởi tạo là loại đối tượng được khởi tạo. Quy tắc thứ hai là để khởi tạo sao chép trong các trường hợp khác.

Vì vậy, từ quan điểm đó, cả hai được gọi là một - quy tắc đầu tiên. Trong trường hợp bạn có bản sao khởi tạo cùng loại, trình biên dịch được phép bỏ qua một bản sao, vì vậy nó có thể xây dựng tạm thời bạn tạo trực tiếp vào đối tượng được khởi tạo. Vì vậy, bạn có thể kết thúc rất tốt với cùng một mã được tạo ra. Nhưng các nhà xây dựng bản sao, ngay cả khi bản sao được elided (tối ưu hóa ra), vẫn phải có sẵn. I.e nếu bạn có một hàm tạo bản sao riêng, mã đó không hợp lệ nếu mã mà nó xuất hiện không có quyền truy cập vào nó.

Thứ hai được gọi là bản sao-khởi, bởi vì nếu loại initializer là của một loại khác nhau, một đối tượng tạm thời được tạo ra trong cố gắng để ngầm chuyển đổi phía bên phải sang bên trái:

myclass c = 3; 

Trình biên dịch tạo ra một đối tượng tạm thời của kiểu của myclass sau đó khi có một hàm tạo có một int. Sau đó, nó khởi tạo đối tượng với tạm thời đó. Cũng trong trường hợp này, tạo tạm thời có thể được tạo trực tiếp trong đối tượng được khởi tạo. Bạn có thể làm theo các bước sau bằng cách in các thông điệp trong các nhà xây dựng/hủy của lớp học của bạn và sử dụng tùy chọn -fno-elide-constructors cho GCC. Nó không cố gắng để elide bản sao sau đó.

Trên một lưu ý phụ, mã ở trên không liên quan gì đến toán tử gán. Trong cả hai trường hợp, điều xảy ra là khởi tạo.

+0

với VC9 "myclass c = 3" dường như chỉ gọi "myclass (int x)" trực tiếp, thay vì sử dụng đối tượng myclass tạm thời. –

+0

VC bị hỏng trong vấn đề này. Nó có một quy tắc tương tự như: "Sử dụng khởi tạo trực tiếp bất cứ khi nào có thể". –

+0

Cảm ơn rất nhiều vì đã giải thích chi tiết, thực sự tôi đã đọc Scott Meyer và thảo luận của Herb Sutter về chủ đề này, nhưng không thể xác minh nó bằng VC++ và GCC, bởi vì mặc định cả trình biên dịch đều bỏ qua bản sao thậm chí tắt tối ưu hóa. Tùy chọn gcc -fno-elide-constructors có giá trị đối với tôi để quan sát ngữ nghĩa chính xác mà không cần bất kỳ tối ưu hóa nào. Và thực tế của "bản sao ctor phải có sẵn" cũng rất hữu ích. – zhaorufei

8

Người thứ hai có thể hoặc không thể gọi thêm myclass đối tượng xây dựng nếu bản sao trình biên dịch không được trình biên dịch của bạn triển khai. Tuy nhiên, hầu hết các nhà xây dựng, đã sao chép elision bật theo mặc định ngay cả khi không có bất kỳ chuyển đổi tối ưu hóa.

Lưu ý khởi tạo trong khi quá trình xây dựng chưa bao giờ gọi toán tử gán.

Luôn luôn, cần lưu ý:

assignment: an already present object gets a new value

initialization: a new object gets a value at the moment it is born.

+0

Điều này không liên quan gì đến NRVO. –

+0

Tôi có nghĩa là các elision, không thể đưa ra đúng thời hạn tại thời điểm đó. Cảm ơn. – dirkgently

5

Trong một giây, một đối tượng tạm thời được tạo ra đầu tiên và sau đó được sao chép vào các đối tượng x sử dụng của myClass copy constructor. Do đó cả hai đều không giống nhau.

4

tôi đã viết như sau để thử và minh họa hiểu những gì đang xảy ra:

#include <iostream> 

using namespace std; 

class myClass 
{ 
public: 
    myClass(int x) 
    { 
     this -> x = x; 
     cout << "int constructor called with value x = " << x << endl; 
    } 

    myClass(const myClass& mc) 
    { 
     cout << "copy constructor called with value = " << mc.x << endl; 
     x = mc.x; 
    } 

    myClass & operator = (const myClass & that) 
    { 
     cout << "assignment called" << endl; 
     if(this != &that) 
     { 
      x = that.x; 
     } 
     return *this; 
    } 

private: 
    int x; 
}; 

int main() 
{ 
    myClass x(3); 
    myClass y = myClass(3); 
} 

Khi tôi biên dịch và chạy mã này tôi nhận được kết quả như sau:

$ ./a.out 
int constructor called with value x = 3 
int constructor called with value x = 3 

sẽ này dường như để cho biết rằng không có sự khác biệt giữa hai cuộc gọi được thực hiện trong chức năng chính, nhưng điều đó sẽ sai. Khi litb chỉ ra, người tạo bản sao phải có sẵn để mã này hoạt động, mặc dù nó được ưu tiên trong trường hợp này. Để chứng minh điều đó, chỉ cần di chuyển hàm tạo bản sao trong đoạn mã ở trên đến phần riêng tư của định nghĩa lớp. Bạn sẽ thấy lỗi sau:

$ g++ myClass.cpp 
myClass.cpp: In function ‘int main()’: 
myClass.cpp:27: error: ‘myClass::myClass(const myClass&)’ is private 
myClass.cpp:37: error: within this context 

Cũng lưu ý rằng toán tử gán là bao giờ gọi.

+0

Nhưng nếu tôi loại bỏ hoàn toàn hàm tạo bản sao khỏi mã đó, nó vẫn hoạt động. Điều đó có nghĩa là gì? – Baltimark

+0

Điều đó ngụ ý rằng trình biên dịch của bạn đang chèn một hàm tạo bản sao mặc định cho bạn –

+0

Chắc chắn, không có vấn đề gì. –