2013-02-25 54 views
34

Tôi đã nghe nói rằng C++ có một cái gì đó gọi là "chuyển đổi constructors" hoặc "chuyển đổi constructors". Những cái này là gì, và chúng là gì? Tôi thấy nó đề cập liên quan đến mã này:Một hàm tạo chuyển đổi trong C++ là gì? Nó là gì?

class MyClass 
{ 
    public: 
    int a, b; 
    MyClass(int i) {} 
} 

int main() 
{ 
    MyClass M = 1 ; 
} 
+3

@MooingDuck: Tôi hiểu ý định, nhưng người dùng nên dành ít thời gian để xây dựng chính xác câu hỏi *. –

+13

Tôi thực sự không hiểu điều gì là sai với câu hỏi này - đó là một câu hỏi tức là các nhà xây dựng chuyển đổi là gì và chúng là gì. Rõ ràng là anh ta đã hỏi nó, vì anh ta không hoàn toàn hiểu được cách sử dụng của họ. Tôi đang học C++, và mỗi lần tôi thấy một khái niệm mới tôi google nó, và đó là cách tôi nhận được câu hỏi này. Nhiều lần tôi nhận được để StackOverFlow để câu hỏi như thế này, giúp tôi hiểu khái niệm. Tôi nghĩ rằng điều này nên được khuyến khích bởi StackOverFlowers. – dan12345

+6

Điên. Tôi đã có cùng một câu hỏi không thực tế. Đoán rằng làm cho nó một câu hỏi toàn bộ. – bvj

Trả lời

39

Định nghĩa cho một công cụ chuyển đổi chuyển đổi khác nhau giữa C++ 03 và C++ 11. Trong cả hai trường hợp, nó phải là một non-explicit constructor (nếu không nó sẽ không được tham gia vào các chuyển đổi ngầm), nhưng đối với C++ 03 nó cũng phải được gọi với một đối số duy nhất. Đó là:

struct foo 
{ 
    foo(int x);    // 1 
    foo(char* s, int x = 0); // 2 
    foo(float f, int x);  // 3 
    explicit foo(char x); // 4 
}; 

Constructors 1 và 2 đều chuyển đổi hàm tạo trong C++ 03 và C++ 11. Constructor 3, mà phải có hai đối số, chỉ là một hàm tạo chuyển đổi trong C++ 11. Cuối cùng, hàm tạo 4, không phải là một hàm tạo chuyển đổi vì nó là explicit.

  • C++ 03: §12.3.1

    Một constructor tuyên bố nếu không có sự chức năng-specifierexplicit có thể được gọi với một tham số duy nhất xác định một chuyển đổi từ các loại tham số đầu tiên của mình để loại lớp của nó. Một hàm tạo như vậy được gọi là một hàm tạo chuyển đổi.

  • C++ 11: §12.3.1

    Một constructor tuyên bố nếu không có sự chức năng-specifierexplicit định một chuyển đổi từ các loại thông số của nó để loại các lớp học . Một hàm tạo như vậy được gọi là một hàm tạo chuyển đổi.

Tại sao các nhà xây dựng với hơn một tham số duy nhất được coi là chuyển đổi nhà xây dựng trong C++ 11? Đó là vì tiêu chuẩn mới cung cấp cho chúng tôi một cú pháp tiện dụng để chuyển các đối số và trả về các giá trị bằng cách sử dụng braced-init-lists. Hãy xem xét ví dụ sau:

foo bar(foo f) 
{ 
    return {1.0f, 5}; 
} 

Khả năng để xác định giá trị trả về là một chuẩn bị tinh thần-init-list được coi là một chuyển đổi. Điều này sử dụng hàm tạo chuyển đổi cho foo có một số floatint. Ngoài ra, chúng tôi có thể gọi chức năng này bằng cách thực hiện bar({2.5f, 10}). Đây cũng là một chuyển đổi. Vì chúng là các chuyển đổi, nên có ý nghĩa đối với các nhà thầu mà họ sử dụng là chuyển đổi các nhà thầu. Điều quan trọng cần lưu ý, do đó, làm cho hàm tạo của foo mất floatint có thông số chức năng explicit sẽ dừng mã ở trên biên dịch. Cú pháp mới ở trên chỉ có thể được sử dụng nếu có một hàm tạo chuyển đổi có sẵn để thực hiện công việc.

  • C++ 11: §6.6.3:

    Một tuyên bố return với một chuẩn bị tinh thần-init-list khởi tạo các đối tượng hoặc tham chiếu được trả về từ hàm bằng cách sao chép -list-initialization (8.5.4) từ danh sách khởi tạo được chỉ định.

    §8.5:

    Việc khởi xảy ra [...] trong tham số đi qua [...] được gọi là bản sao-khởi.

    §12.3.1:

    Một constructor rõ ràng cấu trúc đối tượng giống như nhà xây dựng không rõ ràng, nhưng làm như vậy duy nhất mà cú pháp trực tiếp khởi tạo (8,5) hoặc trong trường hợp phôi (5.2.9, 5.4) được sử dụng một cách rõ ràng.

11

Chuyển đổi ngầm với chuyển đổi constructor

Hãy làm ví dụ trong câu hỏi phức tạp hơn

class MyClass 
{ 
    public: 
    int a, b; 
    MyClass(int i) {} 
    MyClass(const char* n, int k = 0) {} 
    MyClass(MyClass& obj) {} 
} 

hai cấu trúc đầu tiên được chuyển đổi nhà xây dựng . Cái thứ ba là một hàm tạo bản sao và vì vậy nó là một hàm tạo chuyển đổi khác.

Một hàm tạo chuyển đổi cho phép chuyển đổi ngầm từ kiểu đối số sang kiểu hàm tạo. Ở đây, hàm tạo đầu tiên cho phép chuyển đổi từ một đối tượng int thành đối tượng của lớp MyClass. Hàm khởi tạo thứ hai cho phép chuyển đổi từ một chuỗi thành đối tượng của lớp MyClass. Và thứ ba ... từ một đối tượng của lớp MyClass đến một đối tượng của lớp MyClass!

Để là một hàm tạo chuyển đổi, hàm tạo phải có đối số đơn lẻ (trong đối số thứ hai, đối số thứ hai có một giá trị mặc định) và được khai báo không có từ khóa explicit.

Sau đó, khởi tạo trong chính có thể trông như thế này:

int main() 
{ 
    MyClass M = 1 ; 
    // which is an alternative to 
    MyClass M = MyClass(1) ; 

    MyClass M = "super" ; 
    // which is an alternative to 
    MyClass M = MyClass("super", 0) ; 
    // or 
    MyClass M = MyClass("super") ; 
} 

từ khóa Explicit và nhà thầu

Bây giờ, nếu chúng ta đã sử dụng các từ khóa explicit?

class MyClass 
{ 
    public: 
    int a, b; 
    explicit MyClass(int i) {} 
} 

Sau đó, trình biên dịch sẽ không chấp nhận

int main() 
    { 
     MyClass M = 1 ; 
    } 

vì đây là chuyển đổi ngầm. Thay vào đó, phải viết

int main() 
    { 
     MyClass M(1) ; 
     MyClass M = MyClass(1) ; 
     MyClass* M = new MyClass(1) ; 
     MyClass M = (MyClass)1; 
     MyClass M = static_cast<MyClass>(1); 
    } 

explicit từ khóa luôn được sử dụng để ngăn chặn chuyển đổi ngầm cho một constructor và nó áp dụng cho nhà xây dựng trong một khai báo lớp.

+0

Hàm tạo thứ ba trong ví dụ đầu tiên không phải là một hàm tạo bản sao. Đối số của hàm tạo bản sao phải là một trong: 'X &', 'const X &', 'dễ bay hơi X &' hoặc 'const dễ bay hơi X &'. –

+0

Bạn chỉ có thể viết 'MyClass M (1);' v.v. trong ví dụ cuối cùng. Cẩn thận với những chữ số multicharacter đó. – chris

+0

Bạn thậm chí có bận tâm biên dịch mã trước khi đăng không? Các chuỗi * của bạn * thực sự là hằng số multicharacter và không chuyển đổi hoàn toàn thành 'char const *'! – Praetorian

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