2009-04-16 38 views
14

Tôi biết rằng nếu bạn để lại một thành viên trong danh sách khởi tạo trong một hàm tạo no-arg, hàm tạo mặc định của thành viên đó sẽ được gọi.Sao chép danh sách khởi tạo bản dựng

Các nhà xây dựng sao chép cũng có thể gọi hàm tạo bản sao của các thành viên hay họ cũng gọi hàm tạo mặc định?

class myClass { 
    private: 
    someClass a; 
    someOtherClass b; 
    public: 
    myClass() : a(DEFAULT_A) {} //implied is b() 
    myClass(const myClass& mc) : a(mc.a) {} //implied is b(mc.b)??? or is it b()? 
} 
+2

Xem: http://stackoverflow.com/questions/563221/is-there-an-implicit-default-constructor-in-c/563320#563320 –

Trả lời

23

Trình tạo bản sao được xác định rõ ràng không gọi hàm tạo bản sao cho các thành viên.

Khi bạn nhập nội dung của một hàm tạo, mọi thành viên của lớp đó sẽ được khởi tạo. Tức là, khi bạn truy cập vào {, bạn được đảm bảo rằng tất cả các thành viên của bạn đã được khởi tạo.

Trừ khi được chỉ định, các thành viên được khởi tạo mặc định theo thứ tự chúng xuất hiện trong lớp học. (Và nếu chúng không thể được, chương trình là không đúng định dạng.) Vì vậy, nếu bạn xác định constructor sao chép của riêng bạn, nó bây giờ tùy thuộc vào bạn để gọi bất kỳ thành viên sao chép bản sao như mong muốn.

Dưới đây là một chương trình nhỏ, bạn có thể sao chép-dán ở đâu đó và lộn xộn xung quanh với:

#include <iostream> 

class Foo { 
public: 
    Foo() { 
     std::cout << "In Foo::Foo()" << std::endl; 
    } 

    Foo(const Foo& rhs) { 
     std::cout << "In Foo::Foo(const Foo&)" << std::endl; 
    } 
}; 

class Bar { 
public: 
    Bar() { 
     std::cout << "In Bar::Bar()" << std::endl; 
    } 

    Bar(const Bar& rhs) { 
     std::cout << "In Bar::Bar(const Bar&)" << std::endl; 
    } 
}; 

class Baz { 
public: 
    Foo foo; 
    Bar bar; 

    Baz() { 
     std::cout << "In Baz::Baz()" << std::endl; 
    } 

    Baz(const Baz& rhs) { 
     std::cout << "In Baz::Baz(const Baz&)" << std::endl; 
    } 
}; 

int main() { 
    Baz baz1; 
    std::cout << "Copying..." << std::endl; 
    Baz baz2(baz1); 
} 

Như-là, bản in này:

 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz() 
Copying... 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz(const Baz&) 

Lưu ý rằng nó mặc định khởi tạo các thành viên của Baz.

By ý kiến ​​ra constructor sao chép rõ ràng, như:

/* 
Baz(const Baz& rhs) { 
    std::cout << "In Baz::Baz(const Baz&)" << std::endl; 
} 
*/ 

Đầu ra sẽ trở thành này:

 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz() 
Copying... 
In Foo::Foo(const Foo&) 
In Bar::Bar(const Bar&) 

Nó gọi là sao chép constructor trên cả hai.

Và nếu chúng ta giới thiệu lại Baz 's copy constructor và sao chép một cách rõ ràng một thành viên duy nhất:

Baz(const Baz& rhs) : 
    foo(rhs.foo) 
{ 
    std::cout << "In Baz::Baz(const Baz&)" << std::endl; 
} 

Chúng tôi nhận được:

 
In Foo::Foo() 
In Bar::Bar() 
In Baz::Baz() 
Copying... 
In Foo::Foo(const Foo&) 
In Bar::Bar() 
In Baz::Baz(const Baz&) 

Như bạn thấy, một khi bạn tuyên bố một cách rõ ràng sao chép constructor bạn chịu trách nhiệm sao chép tất cả thành viên của lớp; đó là nhà xây dựng của bạn bây giờ.

Điều này áp dụng cho tất cả các nhà thầu, bao gồm cả các nhà thầu di chuyển.

+0

Điều gì sẽ xảy ra nếu một thành viên là một con trỏ thô (ví dụ void *) hoặc int, double, v.v. trước khi nhập {nếu người dùng không gán bất kỳ điều gì cho các thành viên này trong danh sách khởi tạo của hàm tạo bản sao? –

+0

@SergeRogatch: Nếu bạn không khởi tạo nó một cách rõ ràng, giá trị không được chỉ định như một biến không được khởi tạo thông thường và đọc nó là hành vi không xác định. Bạn phải khởi tạo rõ ràng con trỏ thành null, ints thành 0, v.v. – GManNickG

2

Có. Ctors là ctors.

+0

+1, bạn đã đánh bại tôi sau 2 giây. :) –

+1

-1: "Có" có nghĩa là câu trả lời cho câu hỏi có chứa "hoặc"? – mmmmmmmm

+1

rstevens, câu hỏi đã được chỉnh sửa ngay sau khi Charlie trả lời. Charlie trả lời câu hỏi ban đầu một cách hoàn hảo. Điều đó nói rằng, tôi đã chỉnh sửa câu trả lời của tôi dưới đây và tôi nghĩ rằng nó là đủ tốt :) – GManNickG

2

Đối với bất kỳ biến thành viên nào có hàm tạo mặc định, hàm khởi tạo mặc định được gọi nếu bạn chưa thêm bất kỳ lệnh gọi hàm tạo nào khác cho biến thành viên đó vào danh sách khởi tạo.

0

Khi trình biên dịch cung cấp bộ định tuyến mặc định, bạn nghĩ trình biên dịch sẽ làm gì cho các biến thành viên? Nó bản sao cấu trúc nó.

Trong cùng một mạch, nếu CCtor được người dùng xác định và nếu một người bỏ qua một số thành viên thì những thành viên đó không thể được bỏ chạy. Các bất biến lớp được thiết lập trong quá trình xây dựng và phải được duy trì liên tục. Vì vậy, trình biên dịch thực hiện điều đó cho bạn.

+0

-1 xin lỗi. Một trình biên dịch bản sao mặc định do trình biên dịch cung cấp * sẽ * sao chép từng thành viên bằng cách sử dụng ctor bản sao của thành viên đó (đó là bản sao bitwise trong trường hợp các kiểu nguyên thủy). –

+0

Có, nhưng tôi đang nói điều tương tự! * Mặc định Initilalizes * có nghĩa là sao chép thông qua cctor thành viên. – Abhay

+0

Tôi thấy những gì bạn đang nói, nhưng trên thực tế thuật ngữ "khởi tạo mặc định" có ý nghĩa cụ thể, được xác định rõ trong tiêu chuẩn C++, để khởi tạo một đối tượng với giá trị mặc định của loại (tốt, nó hơi phức tạp hơn nhưng anyway ...) Vì vậy, mô tả của bạn là một chút sai lầm. –

1

Không có gì huyền diệu về một hàm tạo bản sao, ngoài trình biên dịch sẽ thêm nó vào nếu cần. Nhưng trong cách nó thực sự chạy, không có gì đặc biệt - nếu bạn không rõ ràng nói "sử dụng như vậy và một nhà xây dựng như vậy", nó sẽ sử dụng mặc định.

1

Không có trong VC9. Không chắc về những người khác.

// compiled as: cl /EHsc contest.cpp 
// 
// Output was: 
// Child1() 
// ----- 
// Child1() 
// Child2() 
// Parent() 
// ----- 
// Child1(Child1&) 
// Child2() 
// Parent(Parent&) 

#include <cstdio> 

class Child1 { 
    int x; 
public: 
    static Child1 DEFAULT; 

    Child1(){ 
     x = 0; 
     printf("Child1()\n"); 
    } 

    Child1(Child1 &other){ 
     x = other.x; 
     printf("Child1(Child1&)\n"); 
    } 
}; 

Child1 Child1::DEFAULT; 

class Child2 { 
    int x; 
public: 
    Child2(){ 
     x = 0; 
     printf("Child2()\n"); 
    } 

    Child2(Child2 &other){ 
     x = other.x; 
     printf("Child2(Child2&)\n"); 
    } 
}; 

class Parent { 
    int x; 
    Child1 c1; 
    Child2 c2; 

public: 
    Parent(){ 
     printf("Parent()\n"); 
    } 

    Parent(Parent &other) : c1(Child1::DEFAULT) { 
     printf("Parent(Parent&)\n"); 
    } 
}; 

int main(){ 
    printf("-----\n"); 
    Parent p1; 
    printf("-----\n"); 
    Parent p2(p1); 

    return 0; 
} 
+0

Và stdout là? –

2

Để biết chi tiết xem: Is there an implicit default constructor in C++?

viết tắt:

  • trình biên dịch tạo "Default Constructor": sử dụng constructor mặc định của từng thành viên.
  • Trình biên dịch tạo ra "Trình tạo bản sao": sử dụng hàm tạo bản sao của mỗi thành viên.
  • Trình biên dịch được tạo "Nhà điều hành chuyển nhượng": sử dụng toán tử gán của từng thành viên.
1

Tùy thuộc vào cách khởi tạo hàm gọi cơ sở, các hàm tạo của thành viên sẽ được gọi theo cùng một cách. Ví dụ, chúng ta hãy bắt đầu với:

struct ABC{ 
    int a; 
    ABC() : a(0) { printf("Default Constructor Called %d\n", a); }; 

    ABC(ABC & other) 
    { 
     a=other.a; 
     printf("Copy constructor Called %d \n" , a) ; 
    }; 
}; 

struct ABCDaddy{ 
    ABC abcchild; 
}; 

Bạn có thể làm các xét nghiệm này:

printf("\n\nTest two, where ABC is a member of another structure\n"); 
ABCDaddy aD; 
aD.abcchild.a=2; 

printf("\n Test: ABCDaddy bD=aD; \n"); 
ABCDaddy bD=aD; // Does call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is called) 

printf("\n Test: ABCDaddy cD(aD); \n"); 
ABCDaddy cD(aD); // Does call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is called) 

printf("\n Test: ABCDaddy eD; eD=aD; \n"); 
ABCDaddy eD; 
eD=aD;   // Does NOT call the copy constructor of the members of the structure ABCDaddy (ie. the copy constructor of ABC is not called) 

Output:

Default Constructor Called 0 

Test: ABCDaddy bD=aD; 
Copy constructor Called 2 

Test: ABCDaddy cD(aD); 
Copy constructor Called 2 

Test: ABCDaddy eD; eD=aD; 
Default Constructor Called 0 

Thưởng thức.

Các vấn đề liên quan