Tìm hiểu thêm về: Trình tạo (C ++)

Bạn đang xem: hàm tạo c ++

là gì

Constructors (C ++)

Trong bài viết này

Để tùy chỉnh cách một lớp khởi tạo các thành viên của nó hoặc để gọi các hàm khi một đối tượng của lớp của bạn được tạo, hãy xác định một phương thức khởi tạo. Một hàm tạo có cùng tên với lớp và không có giá trị trả về. Bạn có thể xác định bao nhiêu hàm tạo được nạp chồng nếu cần để tùy chỉnh quá trình khởi tạo theo nhiều cách khác nhau. Thông thường, các hàm tạo có khả năng truy cập công khai để mã bên ngoài định nghĩa lớp hoặc phân cấp kế thừa có thể tạo các đối tượng của lớp. Nhưng bạn cũng có thể khai báo một hàm tạo là protected hoặc private .

Trình tạo có thể tùy chọn lấy danh sách trình khởi tạo thành viên. Đó là một cách hiệu quả hơn để khởi tạo các thành viên lớp hơn là gán các giá trị trong thân hàm tạo. Ví dụ sau đây cho thấy một lớp Box với ba hàm tạo được nạp chồng. Hai danh sách init thành viên cuối cùng sử dụng:

  class Box {
công cộng:
    // Nhà xây dựng mặc định
    Hộp() {}

    // Khởi tạo một Hộp có kích thước bằng nhau (tức là một khối lập phương)
    Hộp rõ ràng (int i): m_width (i), m_length (i), m_height (i) // danh sách thành viên init
    {}

    // Khởi tạo một Hộp có kích thước tùy chỉnh
    Hộp (int width, int length, int height)
        : m_width (chiều rộng), m_length (chiều dài), m_height (chiều cao)
    {}

    int Volume () {return m_width * m_length * m_height; }

riêng:
    // Sẽ có giá trị bằng 0 khi hàm tạo mặc định được gọi.
    // Nếu chúng ta không zero-init ở đây, hàm tạo mặc định sẽ
    // để chúng chưa khởi tạo với các giá trị rác.
    int m_width {0};
    int m_length {0};
    int m_height {0};
};
 

Khi bạn khai báo một thể hiện của một lớp, trình biên dịch sẽ chọn phương thức khởi tạo nào để gọi dựa trên các quy tắc giải quyết quá tải:

  int main ()
{
    Hộp b; // Hộp cuộc gọi ()

    // Sử dụng khởi tạo thống nhất (ưu tiên):
    Ô b2 {5}; // Hộp lệnh gọi (int)
    Ô b3 {5, 8, 12}; // Hộp lệnh gọi (int, int, int)

    // Sử dụng ký hiệu kiểu hàm:
    Ô b4 (2, 4, 6); // Hộp lệnh gọi (int, int, int)
}
 
  • Các trình xây dựng có thể được khai báo là inline , rõ ràng , friend hoặc constexpr .
  • Một phương thức khởi tạo có thể khởi tạo một đối tượng đã được khai báo là const , variable hoặc < mã> const dễ bay hơi . Đối tượng trở thành const sau khi phương thức khởi tạo hoàn thành.
  • Để xác định một hàm tạo trong tệp triển khai, hãy đặt cho nó một tên đủ điều kiện giống như bất kỳ hàm thành viên nào khác: Box :: Box () {...} .

Danh sách trình khởi tạo thành viên

Một phương thức khởi tạo có thể tùy chọn có danh sách bộ khởi tạo thành viên, danh sách này khởi tạo các thành viên lớp trước khi phần thân hàm tạo chạy. (Danh sách trình khởi tạo thành viên không giống với danh sách trình khởi tạo thuộc loại std :: initializer_list & lt; T & gt; .)

Ưu tiên danh sách bộ khởi tạo thành viên hơn là gán giá trị trong phần thân của hàm tạo. Danh sách khởi tạo thành viên trực tiếp khởi tạo các thành viên. Ví dụ sau đây cho thấy danh sách trình khởi tạo thành viên, bao gồm tất cả các biểu thức định danh (đối số) sau dấu hai chấm:

  Hộp (int width, int length, int height)
        : m_width (chiều rộng), m_length (chiều dài), m_height (chiều cao)
    {}
 

Định danh phải tham chiếu đến một thành viên trong lớp; nó được khởi tạo với giá trị của đối số. Đối số có thể là một trong các tham số hàm tạo, một lệnh gọi hàm hoặc một std :: initializer_list & lt; T & gt; .

const thành viên và thành viên của loại tham chiếu phải được khởi tạo trong danh sách khởi tạo thành viên.

Để đảm bảo các lớp cơ sở được khởi tạo đầy đủ trước khi chạy hàm tạo dẫn xuất, hãy gọi mọi hàm tạo lớp cơ sở được tham số hóa trong danh sách bộ khởi tạo.

Các hàm tạo mặc định

Các hàm tạo mặc định thường không có tham số, nhưng chúng có thể có các tham số với giá trị mặc định.

 class Box {
công cộng:
    Hộp () {/ * thực hiện bất kỳ bước khởi tạo mặc định bắt buộc nào * /}

    // Tất cả các tham số đều có giá trị mặc định
    Hộp (int w = 1, int l = 1, int h = 1): m_width (w), m_height (h), m_length (l) {}
...
}
 

Các hàm tạo mặc định là một trong những chức năng thành viên đặc biệt . Nếu không có hàm tạo nào được khai báo trong một lớp, trình biên dịch sẽ cung cấp một hàm tạo mặc định inline ngầm định.

  # bao gồm & lt; iostream & gt;
sử dụng không gian tên std;

Hộp lớp {
công cộng:
    int Volume () {return m_width * m_height * m_length;}
riêng:
    int m_width {0};
    int m_height {0};
    int m_length {0};
};

int main () {
    Hộp hộp1; // Gọi hàm tạo do trình biên dịch tạo
    cout & lt; & lt; "box1.Volume:" & lt; & lt; box1.Volume () & lt; & lt; endl; // Kết quả đầu ra 0
}
 

Nếu bạn dựa vào một hàm tạo mặc định ngầm định, hãy đảm bảo khởi tạo các thành viên trong định nghĩa lớp, như được hiển thị trong ví dụ trước. Nếu không có các trình khởi tạo đó, các thành viên sẽ không được khởi tạo và lệnh gọi Khối lượng () sẽ tạo ra một giá trị rác. Nói chung, bạn nên khởi tạo các thành viên theo cách này ngay cả khi không dựa vào hàm tạo mặc định ngầm định.

Bạn có thể ngăn trình biên dịch tạo một phương thức khởi tạo ngầm định mặc định bằng cách xác định nó là đã bị xóa :

  // Hàm tạo mặc định
    Hộp () = xóa;
 

Một phương thức khởi tạo mặc định do trình biên dịch tạo ra sẽ được định nghĩa là bị xóa nếu bất kỳ thành viên nào của lớp không có cấu trúc mặc định. Ví dụ: tất cả các thành viên của kiểu lớp và các thành viên kiểu lớp của chúng, phải có một hàm tạo và hàm hủy mặc định có thể truy cập được. Tất cả các thành viên dữ liệu thuộc loại tham chiếu và tất cả các thành viên const phải có bộ khởi tạo thành viên mặc định.

Khi bạn gọi một hàm tạo mặc định do trình biên dịch tạo và cố gắng sử dụng dấu ngoặc đơn, một cảnh báo sẽ được đưa ra:

  lớp myclass {};
int main () {
myclass mc (); // cảnh báo C4930: hàm nguyên mẫu không được gọi (có phải là định nghĩa biến không?)
}
 

Tuyên bố này là một ví dụ về vấn đề “Phân tích cú pháp gây khó chịu nhất”. Bạn có thể diễn giải myclass md (); dưới dạng khai báo hàm hoặc là lệnh gọi của một phương thức khởi tạo mặc định. Bởi vì trình phân tích cú pháp C ++ ưu tiên khai báo hơn những thứ khác, biểu thức được coi như một khai báo hàm. Để biết thêm thông tin, hãy xem Phân tích cú pháp gây khó chịu nhất .

Nếu bất kỳ hàm tạo không mặc định nào được khai báo, trình biên dịch sẽ không cung cấp hàm tạo mặc định:

  class Box {
công cộng:
    Hộp (int width, int length, int height)
        : m_width (chiều rộng), m_length (chiều dài), m_height (chiều cao) {}
riêng:
    int m_ width;
    int m_length;
    int m_height;

};

int main () {

    Hộp hộp1 (1, 2, 3);
    Hộp hộp2 {2, 3, 4};
    Hộp hộp3; // C2512: không có sẵn hàm tạo mặc định thích hợp
}
 

Nếu một lớp không có hàm tạo mặc định, thì một mảng các đối tượng của lớp đó không thể được tạo bằng cách sử dụng cú pháp dấu ngoặc vuông. Ví dụ: với khối mã trước đó, một mảng Hộp không thể được khai báo như thế này:

  Hộp hộp [3]; // C2512: không có sẵn hàm tạo mặc định thích hợp
 

Tuy nhiên, bạn có thể sử dụng một tập hợp danh sách trình khởi tạo để khởi tạo một mảng đối tượng Box:

  Hộp hộp [3] {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
 

Để biết thêm thông tin, hãy xem Trình khởi tạo .

Sao chép các hàm tạo

Một phương thức khởi tạo sao chép khởi tạo một đối tượng bằng cách sao chép các giá trị thành viên từ một đối tượng cùng loại. Nếu các thành viên trong lớp của bạn là tất cả các kiểu đơn giản chẳng hạn như giá trị vô hướng, thì hàm tạo bản sao do trình biên dịch tạo là đủ và bạn không cần phải xác định hàm của riêng mình. Nếu lớp của bạn yêu cầu khởi tạo phức tạp hơn, thì bạn cần triển khai một phương thức khởi tạo sao chép tùy chỉnh. Ví dụ: nếu một thành viên lớp là một con trỏ thì bạn cần phải xác định một phương thức khởi tạo sao chép để cấp phát bộ nhớ mới và sao chép các giá trị từ đối tượng trỏ tới của đối tượng khác. Phương thức khởi tạo sao chép do trình biên dịch tạo ra chỉ cần sao chép con trỏ, để con trỏ mới vẫn trỏ đến vị trí bộ nhớ của con trỏ khác.

Phương thức tạo bản sao có thể có một trong các chữ ký sau:

  Hộp (Hộp & amp; khác); // Tránh nếu có thể - cho phép sửa đổi khác.
    Hộp (const Box & amp; other);
    Hộp (Hộp dễ bay hơi & amp; khác);
    Hộp (Hộp const dễ bay hơi & amp; khác);

    // Tham số bổ sung OK nếu chúng có giá trị mặc định
    Hộp (Hộp & amp; khác, int i = 42, string label = "Hộp");
 

Khi bạn xác định một hàm tạo bản sao, bạn cũng nên xác định một toán tử gán bản sao (=). Để biết thêm thông tin, hãy xem Chuyển nhượng Sao chép trình tạo và sao chép toán tử chuyển nhượng .

Bạn có thể ngăn đối tượng của mình bị sao chép bằng cách xác định hàm tạo bản sao là đã bị xóa:

  Box (const Box & amp; other) = delete;
 

Cố gắng sao chép đối tượng tạo ra lỗi C2280: cố gắng tham chiếu đến một hàm đã bị xóa.

Di chuyển các hàm tạo

Một phương thức khởi tạo di chuyển là một hàm thành viên đặc biệt di chuyển quyền sở hữu dữ liệu của đối tượng hiện có sang một biến mới mà không cần sao chép dữ liệu gốc. Nó lấy tham chiếu rvalue làm tham số đầu tiên và mọi tham số sau này phải có giá trị mặc định. Các hàm tạo Move có thể làm tăng đáng kể hiệu quả chương trình của bạn khi truyền xung quanh các đối tượng lớn.

  Hộp (Hộp & amp; & amp; khác);
 

Trình biên dịch chọn một phương thức khởi tạo di chuyển khi đối tượng được khởi tạo bởi một đối tượng khác cùng loại, nếu đối tượng kia sắp bị phá hủy và không còn cần tài nguyên của nó nữa. Ví dụ sau đây cho thấy một trường hợp khi một phương thức khởi tạo di chuyển được chọn bằng độ phân giải quá tải. Trong hàm tạo gọi get_Box () , giá trị trả về là xvalue (giá trị eXpiring). Nó không được gán cho bất kỳ biến nào và do đó sắp vượt ra khỏi phạm vi. Để cung cấp động lực cho ví dụ này, hãy cung cấp cho Box một vectơ lớn gồm các chuỗi biểu thị nội dung của nó. Thay vì sao chép vectơ và các chuỗi của nó, hàm tạo di chuyển “đánh cắp” nó khỏi “hộp” giá trị sắp hết hạn để vectơ bây giờ thuộc về đối tượng mới. Lệnh gọi tới std :: move là tất cả những gì cần thiết vì cả hai lớp vector string đều triển khai các hàm tạo di chuyển của riêng chúng.

  # bao gồm & lt; iostream & gt;
#include & lt; vector & gt;
#include & lt; string & gt;
#include & lt; thuật toán & gt;
sử dụng không gian tên std;

Hộp lớp {
công cộng:
    Hộp () {std :: cout & lt; & lt; "mặc định" & lt; & lt; std :: endl; }
    Hộp (int width, int height, int length)
       : m_width (chiều rộng), m_height (chiều cao), m_length (chiều dài)
    {
        std :: cout & lt; & lt; "int, int, int" & lt; & lt; std :: endl;
    }
    Hộp (Hộp & amp; khác)
       : m_width (other.m_width), m_height (other.m_height), m_length (other.m_length)
    {
        std :: cout & lt; & lt; "sao chép" & lt; & lt; std :: endl;
    }
    Hộp (Hộp & amp; & amp; khác): m_width (other.m_width), m_height (other.m_height), m_length (other.m_length)
    {
        m_contents = std :: move (other.m_contents);
        std :: cout & lt; & lt; "di chuyển" & lt; & lt; std :: endl;
    }
    int Volume () {return m_width * m_height * m_length; }
    void Add_Item (mục chuỗi) {m_contents.push_back (mục); }
    void Print_Contents ()
    {
        for (const auto & amp; item: m_contents)
        {
            cout & lt; & lt; mục & lt; & lt; "";
        }
    }
riêng:
    int m_width {0};
    int m_height {0};
    int m_length {0};
    vectơ & lt; string & gt; m_nội dung;
};

Hộp get_Box ()
{
    Ô b (5, 10, 18); // "int, int, int"
    b.Add_Item ("Du lịch");
    b.Add_Item ("Loa");
    b.Add_Item ("Bộ đồ");

    trả lại b;
}

int main ()
{
    Hộp b; // "mặc định"
    Ô b1 (b); // "sao chép"
    Hộp b2 (get_Box ()); // "di chuyển"
    cout & lt; & lt; "b2 nội dung:";
    b2.Print_Contents (); // Chứng minh rằng chúng ta có tất cả các giá trị

    char ch;
    cin & gt; & gt; ch & agrave; // giữ cho cửa sổ mở
    trả về 0;
}
 

Nếu một lớp không xác định phương thức khởi tạo di chuyển, trình biên dịch sẽ tạo ra một phương thức ẩn nếu không có phương thức khởi tạo sao chép do người dùng khai báo, toán tử gán sao chép, toán tử gán di chuyển hoặc hàm hủy. Nếu không có phương thức khởi tạo di chuyển rõ ràng hoặc ngầm định nào được xác định, các hoạt động sẽ sử dụng phương thức khởi tạo di chuyển sẽ sử dụng phương thức khởi tạo sao chép thay thế. Nếu một lớp khai báo hàm tạo di chuyển hoặc toán tử gán di chuyển, thì hàm tạo sao chép được khai báo ngầm định sẽ được định nghĩa là đã xóa.

Một hàm tạo di chuyển được khai báo ngầm được định nghĩa là bị xóa nếu bất kỳ thành viên nào thuộc loại lớp thiếu hàm hủy hoặc nếu trình biên dịch không thể xác định hàm tạo nào sẽ sử dụng cho hoạt động di chuyển.

Để biết thêm thông tin về cách viết một hàm tạo chuyển động không tầm thường, hãy xem Hàm chuyển hàm và toán tử chuyển nhiệm vụ (C ++) .

Các hàm tạo đã xóa và mặc định rõ ràng

Bạn có thể mặc định rõ ràng các hàm tạo sao chép, các hàm tạo mặc định, các hàm tạo di chuyển, các toán tử gán sao chép, di chuyển các toán tử gán và các hàm hủy. Bạn có thể xóa tất cả các chức năng đặc biệt của thành viên một cách rõ ràng.

  class Box2
{
công cộng:
    Box2 () = xóa;
    Box2 (const Box2 & amp; other) = default;
    Hộp2 & amp; toán tử = (const Box2 & amp; khác) = mặc định;
    Box2 (Box2 & amp; & amp; other) = default;
    Hộp2 & amp; operator = (Box2 & amp; & amp; other) = default;
    // ...
};
 

Để biết thêm thông tin, hãy xem Các chức năng được mặc định và xóa rõ ràng .

hàm tạo constexpr

Một phương thức khởi tạo có thể được khai báo là constexpr nếu

  • nó được khai báo là mặc định hoặc nếu nó đáp ứng tất cả các điều kiện cho chức năng constexpr nói chung;
  • lớp không có lớp cơ sở ảo;
  • mỗi tham số là một kiểu chữ ;
  • phần thân không phải là một khối thử chức năng;
  • tất cả các thành viên dữ liệu không tĩnh và các subobject của lớp cơ sở đều được khởi tạo;
  • nếu lớp học là (a) một liên minh có các thành viên khác nhau hoặc (b) có các công đoàn ẩn danh, thì chỉ một trong số các thành viên liên minh được khởi tạo;
  • mọi thành viên dữ liệu không tĩnh của loại lớp và tất cả các subobject của lớp cơ sở đều có hàm tạo constexpr

Công cụ tạo danh sách trình khởi tạo

Nếu một hàm tạo nhận std :: initializer_list & lt; T & gt; làm tham số của nó và bất kỳ tham số nào khác có đối số mặc định, thì hàm tạo đó được chọn trong độ phân giải quá tải khi lớp được khởi tạo thông qua khởi tạo trực tiếp. Bạn có thể sử dụng initializer_list để khởi tạo bất kỳ thành viên nào có thể chấp nhận nó. Ví dụ: giả sử lớp Box (được hiển thị trước đó) có std :: vector & lt; string & gt; member m_contents . Bạn có thể cung cấp một hàm tạo như sau:

  Box (initializer_list & lt; string & gt; list, int w = 0, int h = 0, int l = 0)
        : m_contents (list), m_width (w), m_height (h), m_length (l)
{}
 

Và sau đó tạo các đối tượng Box như sau:

  Hộp b {"táo", "cam", "lê"}; // hoặc ...
    Hộp b2 (khởi tạo_danh sách & lt; string & gt; {"bread", "cheese", "wine"}, 2, 4, 6);
 

Các hàm tạo rõ ràng

Nếu một lớp có một phương thức khởi tạo với một tham số duy nhất hoặc nếu tất cả các tham số ngoại trừ một tham số đều có giá trị mặc định, thì kiểu tham số có thể được chuyển đổi hoàn toàn thành kiểu lớp. Ví dụ: nếu lớp Box có một hàm tạo như sau:

  Box (int size): m_width (size), m_length (size), m_height (size) {}
 

Có thể khởi tạo một Hộp như thế này:

  Hộp b = 42;
 

Hoặc chuyển một số nguyên vào một hàm có Hộp:

  class ShippingOrder
{
công cộng:
    ShippingOrder (Hộp b, bưu phí kép): m_box (b), m_postage (bưu phí) {}

riêng:
    Hộp m_hộp;
    double m_postage;
}
// nơi khác ...
    ShippingOrder so (42, 10.8);
 

Những chuyển đổi như vậy có thể hữu ích trong một số trường hợp, nhưng thường thì chúng có thể dẫn đến các lỗi nhỏ nhưng nghiêm trọng trong mã của bạn. Theo nguyên tắc chung, bạn nên sử dụng từ khóa rõ ràng trên một hàm tạo (và các toán tử do người dùng xác định) để ngăn loại chuyển đổi kiểu ngầm này:

  Hộp rõ ràng (int size): m_width (size), m_length (size), m_height (size) {}
 

Khi hàm tạo là rõ ràng, dòng này gây ra lỗi trình biên dịch: ShippingOrder so (42, 10.8); . Để biết thêm thông tin, hãy xem Chuyển đổi loại do người dùng xác định .

Trình tự xây dựng

Một hàm tạo thực hiện công việc của nó theo thứ tự sau:

  1. Nó gọi lớp cơ sở và các hàm tạo thành viên theo thứ tự khai báo.

  2. Nếu lớp có nguồn gốc từ các lớp cơ sở ảo, nó sẽ khởi tạo các con trỏ cơ sở ảo của đối tượng.

  3. Nếu lớp có hoặc kế thừa các hàm ảo, nó sẽ khởi tạo các con trỏ hàm ảo của đối tượng. Con trỏ hàm ảo trỏ đến bảng hàm ảo của lớp để cho phép liên kết chính xác các lệnh gọi hàm ảo với mã.

  4. Nó thực thi bất kỳ mã nào trong phần thân hàm của nó.

Ví dụ sau đây cho thấy thứ tự mà lớp cơ sở và các hàm tạo thành viên được gọi trong hàm tạo cho một lớp dẫn xuất. Đầu tiên, hàm tạo cơ sở được gọi. Sau đó, các thành viên của lớp cơ sở được khởi tạo theo thứ tự mà chúng xuất hiện trong khai báo lớp. Cuối cùng, hàm tạo dẫn xuất được gọi.

  # bao gồm & lt; iostream & gt;

sử dụng không gian tên std;

lớp Chứa1 {
công cộng:
    Chứa1 () {cout & lt; & lt; "Chứa1 ctor \ n"; }
};

lớp Chứa2 {
công cộng:
    Chứa2 () {cout & lt; & lt; "Chứa2 ctor \ n"; }
};

lớp Chứa3 {
công cộng:
    Chứa3 () {cout & lt; & lt; "Đã chứa 3 ctor \ n"; }
};

lớp BaseContainer {
công cộng:
    BaseContainer () {cout & lt; & lt; "BaseContainer ctor \ n"; }
riêng:
    Chứa1 c1;
    Chứa2 c2;
};

class DerivedContainer: public BaseContainer {
công cộng:
    DerivedContainer (): BaseContainer () {cout & lt; & lt; "DerivedContainer ctor \ n"; }
riêng:
    Chứa3 c3;
};

int main () {
    DerivedContainer dc;
}
 

Đây là kết quả:

  Chứa 1 ctor
Chứa 2 ctor
BaseContainer ctor
Chứa 3 ctor
DerivedContainer ctor
 

Một hàm tạo lớp dẫn xuất luôn gọi một hàm tạo lớp cơ sở, để nó có thể dựa vào các lớp cơ sở được xây dựng hoàn chỉnh trước khi thực hiện bất kỳ công việc bổ sung nào. Các hàm tạo lớp cơ sở được gọi theo thứ tự dẫn xuất — ví dụ: nếu ClassA có nguồn gốc từ ClassB , được dẫn xuất từ ​​ ClassC , thì < code> ClassC constructor được gọi đầu tiên, sau đó đến constructor ClassB , sau đó là constructor ClassA .

Nếu một lớp cơ sở không có hàm tạo mặc định, bạn phải cung cấp các tham số hàm tạo của lớp cơ sở trong hàm tạo của lớp dẫn xuất:

  class Box {
công cộng:
    Hộp (int width, int length, int height) {
       m_width = chiều rộng;
       m_length = chiều dài;
       m_height = chiều cao;
    }

riêng:
    int m_ width;
    int m_length;
    int m_height;
};

class StorageBox: public Box {
công cộng:
    StorageBox (int width, int length, int height, const string label & amp;): Box (width, length, height) {
        m_label = nhãn;
    }
riêng:
    chuỗi m_label;
};

int main () {

    const string aLabel = "aLabel";
    StorageBox sb (1, 2, 3, aLabel);
}
 

Nếu một hàm tạo ném ra một ngoại lệ, thứ tự hủy sẽ ngược lại với thứ tự xây dựng:

  1. Mã trong phần thân của hàm tạo chưa được gắn.

  2. Lớp cơ sở và các đối tượng thành viên bị hủy theo thứ tự khai báo ngược lại.

  3. Nếu phương thức khởi tạo không được ủy quyền, tất cả các đối tượng và thành viên của lớp cơ sở được xây dựng đầy đủ sẽ bị hủy. Tuy nhiên, vì bản thân đối tượng không được tạo hoàn chỉnh nên trình hủy không chạy.

Các hàm tạo có nguồn gốc và khởi tạo tổng hợp mở rộng

Nếu phương thức khởi tạo của lớp cơ sở là không công khai, nhưng có thể truy cập được đối với lớp dẫn xuất, thì bạn không thể sử dụng dấu ngoặc nhọn để khởi tạo đối tượng thuộc kiểu dẫn xuất trong / std: c + +17 chế độ trở lên trong Visual Studio 2017 trở lên.

Ví dụ sau cho thấy hành vi tuân thủ C ++ 14:

  struct Bắt nguồn;

cơ sở struct {
    kết cấu bạn bè Có nguồn gốc;
riêng:
    Cơ sở() {}
};

struct Derived: Base {};

Xuất phát d1; // ĐƯỢC RỒI. Không liên quan đến init tổng hợp.
Xuất phát d2 {}; // OK trong C ++ 14: Cuộc gọi Derived :: Derived ()
               // có thể gọi Base ctor.
 

Trong C ++ 17, Derived hiện được coi là một kiểu tổng hợp. Điều đó có nghĩa là việc khởi tạo Base thông qua phương thức khởi tạo mặc định riêng sẽ diễn ra trực tiếp, như một phần của quy tắc khởi tạo tổng hợp mở rộng. Trước đây, phương thức khởi tạo private Base được gọi thông qua phương thức khởi tạo Derived và nó đã thành công do khai báo friend .

Ví dụ sau cho thấy hành vi C ++ 17 trong Visual Studio 2017 trở lên ở chế độ / std: c ++ 17 :

  struct Bắt nguồn;

cơ sở struct {
    kết cấu bạn bè Có nguồn gốc;
riêng:
    Cơ sở() {}
};

struct Derived: Base {
    Derived () {} // thêm hàm tạo do người dùng xác định
                 // để gọi với {} khởi tạo
};

Xuất phát d1; // ĐƯỢC RỒI. Không liên quan đến init tổng hợp.

Xuất phát d2 {}; // lỗi C2248: 'Base :: Base': không thể truy cập
               // thành viên private được khai báo trong lớp 'Base'
 

Các hàm tạo cho các lớp có đa kế thừa

Nếu một lớp được dẫn xuất từ ​​nhiều lớp cơ sở, thì các hàm tạo của lớp cơ sở được gọi theo thứ tự mà chúng được liệt kê trong khai báo của lớp dẫn xuất:

  # bao gồm & lt; iostream & gt;
sử dụng không gian tên std;

lớp BaseClass1 {
công cộng:
    BaseClass1 () {cout & lt; & lt; "BaseClass1 ctor \ n"; }
};
lớp BaseClass2 {
công cộng:
    BaseClass2 () {cout & lt; & lt; "BaseClass2 ctor \ n"; }
};
lớp BaseClass3 {
công cộng:
    BaseClass3 () {cout & lt; & lt; "BaseClass3 ctor \ n"; }
};
class DerivedClass: public BaseClass1,
                     public BaseClass2,
                     public BaseClass3
                     {
công cộng:
    DerivedClass () {cout & lt; & lt; "DerivedClass ctor \ n"; }
};

int main () {
    DerivedClass dc;
}
 

Bạn sẽ mong đợi kết quả sau:

  BaseClass1 ctor
BaseClass2 ctor
BaseClass3 ctor
DerivedClass ctor
 

Ủy quyền các hàm tạo

Một hàm tạo ủy quyền gọi một hàm tạo khác trong cùng một lớp để thực hiện một số công việc khởi tạo. Tính năng này hữu ích khi bạn có nhiều hàm tạo mà tất cả đều phải thực hiện công việc tương tự. Bạn có thể viết logic chính trong một hàm tạo và gọi nó từ những hàm khác. Trong ví dụ đơn giản sau, Box (int) ủy quyền công việc của nó cho Box (int, int, int):

 class Box {
công cộng:
    // Nhà xây dựng mặc định
    Hộp() {}

    // Khởi tạo một Hộp có kích thước bằng nhau (tức là một khối lập phương)
    Box (int i): Box (i, i, i) // hàm tạo ủy quyền
    {}

    // Khởi tạo một Hộp có kích thước tùy chỉnh
    Hộp (int width, int length, int height)
        : m_width (chiều rộng), m_length (chiều dài), m_height (chiều cao)
    {}
    // ... phần còn lại của lớp như trước
};
 

Đối tượng được tạo bởi các hàm tạo sẽ được khởi tạo hoàn toàn ngay sau khi bất kỳ hàm tạo nào kết thúc. Để biết thêm thông tin, hãy xem phần Ủy quyền người xây dựng .

Các hàm tạo kế thừa (C ++ 11)

Một lớp dẫn xuất có thể kế thừa các hàm tạo từ một lớp cơ sở trực tiếp bằng cách sử dụng khai báo using như thể hiện trong ví dụ sau:

  # bao gồm & lt; iostream & gt;
sử dụng không gian tên std;

lớp cơ sở
{
công cộng:
    Căn cứ () {cout & lt; & lt; "Cơ sở ()" & lt; & lt; endl; }
    Cơ sở (const Base & amp; other) {cout & lt; & lt; "Cơ sở (Base & amp;)" & lt; & lt; endl; }
    rõ ràng Cơ sở (int i): num (i) {cout & lt; & lt; "Cơ sở (int)" & lt; & lt; endl; }
    rõ ràng Căn cứ (char c): letter (c) {cout & lt; & lt; "Cơ sở (ký tự)" & lt; & lt; endl; }

riêng:
    int num;
    ký tự char;
};

class Derived: Base
{
công cộng:
    // Kế thừa tất cả các hàm tạo từ Base
    sử dụng Base :: Base;

riêng:
    // Không thể khởi tạo newMember từ các hàm tạo Cơ sở.
    int newMember {0};
};

int main ()
{
    cout & lt; & lt; "Xuất phát d1 (5) gọi:";
    Xuất phát d1 (5);
    cout & lt; & lt; "Xuất phát d1 ('c') gọi:";
    Xuất phát d2 ('c');
    cout & lt; & lt; "Xuất phát d3 = d2 gọi:";
    Xuất phát d3 = d2;
    cout & lt; & lt; "Các cuộc gọi d4 có nguồn gốc:";
    Xuất phát d4;
}

/ * Đầu ra:
Các lệnh gọi d1 (5) phát sinh: Base (int)
Các lệnh gọi d1 ('c') phát sinh: Base (char)
Lệnh gọi d3 = d2 phát sinh: Cơ sở (Cơ sở & amp;)
Lệnh gọi d4 phát sinh: Base () * /
 

Visual Studio 2017 trở lên : Câu lệnh using trong / std: c ++ 17 và sau đó đưa vào phạm vi tất cả các hàm tạo từ lớp cơ sở ngoại trừ các hàm có chữ ký giống hệt với các hàm tạo trong lớp dẫn xuất. Nói chung, tốt nhất nên sử dụng các hàm tạo kế thừa khi lớp dẫn xuất không khai báo thành viên dữ liệu hoặc hàm tạo mới.

Một mẫu lớp có thể kế thừa tất cả các hàm tạo từ một đối số kiểu nếu kiểu đó chỉ định một lớp cơ sở:

  mẫu & lt; tên kiểu T & gt;
lớp Bắt nguồn: T {
    sử dụng T :: T; // khai báo các hàm tạo từ T
    // ...
};
 

Một lớp dẫn xuất không thể kế thừa từ nhiều lớp cơ sở nếu các lớp cơ sở đó có các hàm tạo có chữ ký giống hệt nhau.

Các hàm tạo và các lớp kết hợp

Các lớp có chứa các thành viên kiểu lớp được gọi là các lớp hỗn hợp. Khi một thành viên kiểu lớp của một lớp tổng hợp được tạo, hàm tạo được gọi trước hàm tạo của chính lớp đó. Khi một lớp được chứa thiếu một phương thức khởi tạo mặc định, bạn phải sử dụng danh sách khởi tạo trong phương thức khởi tạo của lớp tổng hợp. Trong ví dụ StorageBox trước đó, nếu bạn thay đổi loại biến thành viên m_label thành một lớp Label mới, bạn phải gọi cả lớp cơ sở phương thức khởi tạo và khởi tạo biến m_label trong phương thức khởi tạo StorageBox :

  nhãn lớp {
công cộng:
    Nhãn (const string & amp; name, const string & amp; address) {m_name = name; m_address = địa chỉ; }
    chuỗi m_name;
    chuỗi m_address;
};

class StorageBox: public Box {
công cộng:
    StorageBox (int width, int length, int height, Label label)
        : Hộp (chiều rộng, chiều dài, chiều cao), m_label (nhãn) {}
riêng:
    Nhãn m_label;
};

int main () {
// truyền một Nhãn có tên
    Nhãn label1 {"some_name", "some_address"};
    StorageBox sb1 (1, 2, 3, label1);

    // chuyển một nhãn tạm thời
    StorageBox sb2 (3, 4, 5, Nhãn {"tên khác", "địa chỉ khác"});

    // chuyển nhãn tạm thời làm danh sách trình khởi tạo
    StorageBox sb3 (1, 2, 3, {"myname", "myaddress"});
}
 

Trong phần này

Xem thêm

Các lớp và cấu trúc


Xem thêm những thông tin liên quan đến chủ đề hàm tạo c ++ là gì

C++ 25: Tại sao hàm main() lại return 0?

  • Tác giả: thân triệu
  • Ngày đăng: 2019-07-01
  • Đánh giá: 4 ⭐ ( 2931 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Hướng dẫn lập trình C++ cơ bản. Học lập trình C++ cho tất cả mọi người. C++ cập nhật và đầy đủ trên kênh thân triệu. Tại sao hàm main lại return 0? thân triệu channel-let's grow together.

Tạo hàm (function) trong C

  • Tác giả: mylop.edu.vn
  • Đánh giá: 3 ⭐ ( 6899 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Trong bài này chúng ta sẽ tìm hiểu về function trong ngôn ngữ C, giúp bạn hiểu rõ function là gì và tại sao nên sử dụng nó khi lập trình.

Tạo hàm (function) trong C - Freetuts

  • Tác giả: hoctapsgk.com
  • Đánh giá: 4 ⭐ ( 3688 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Trong bài này chúng ta sẽ tìm hiểu về hàm (function) trong ngôn ngữ C, cách tạo hàm - khai báo hàm - tham số truyền vào và giá trị tra về của hàm

Hàm khởi tạo trong C++ » Cafedev.vn

  • Tác giả: cafedev.vn
  • Đánh giá: 5 ⭐ ( 1016 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Constructor là một loại hàm thành viên đặc biệt của class, được gọi tự động khi một đối tượng của class đó được khởi tạo. Các constructors thường được sử dụng để khởi tạo các biến thành viên của class theo các giá trị mặc định phù hợp hoặc do người dùng cung cấp, hoặc để thực hiện bất kỳ các bước thiết lập cần thiết nào cho class (ví dụ: Mở file hoặc cơ sở dữ liệu).

Class trong C++: Tạo Lớp và Đối tượng trong C++ Cơ bản

  • Tác giả: ironhackvietnam.edu.vn
  • Đánh giá: 3 ⭐ ( 6141 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Class (Lớp) trong C++ thực chất là gì? Làm sao để tạo các lớp và đối tượng? Cách sử dụng chúng trong C++ như thế nào? Xem ngay kẻo lỡ!

Bảng băm trong C++

  • Tác giả: topdev.vn
  • Đánh giá: 3 ⭐ ( 5550 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Bảng băm hay HashTable là một cấu trúc mà khi người dùng thực hiện truy xuất một phần tử qua khóa thì nó sẽ được ánh xạ vào thông qua hàm băm (Hash function).

Constructor Trong C++

  • Tác giả: techacademy.edu.vn
  • Đánh giá: 3 ⭐ ( 4105 lượt đánh giá )
  • Khớp với kết quả tìm kiếm: Trong bài học hôm nay chúng ta sẽ cùng tìm hiểu về hàm xây dựng (constructor) trong C++ nhé. Vậy hàm xây dựng (constructor) trong C++ là gì? Nó được sử dụng như thế nào? Nó có khác gì so với các hàm bình thường trong C++. I. Constructor Trong C++ Là Gì + Constructor […]

Xem thêm các bài viết khác thuộc chuyên mục: Kiến thức lập trình

Xem Thêm  Cách kết hợp hoặc hợp nhất các ô trong bảng HTML - bảng td hợp nhất các ô

By ads_php