Làm cách nào để khởi tạo các biến thành viên của lớp cơ sở trong phương thức khởi tạo của lớp dẫn xuất?


123

Tại sao tôi không thể làm điều này?

class A
{
public:
    int a, b;
};

class B : public A
{
    B() : A(), a(0), b(0)
    {
    }

};

7
Bạn đang hỏi tại sao bạn không thể làm điều đó, đó là một câu hỏi về ngôn ngữ-thiết kế, hay bạn đang hỏi làm thế nào để giải quyết vấn đề hạn chế về ngôn ngữ đó?
Rob Kennedy

Tôi nghĩ rằng có một số cách đặc biệt để làm điều đó mà tôi không biết, mà không cần phải sử dụng hàm tạo cơ sở.
amrhassan

Các thành viên lớp cơ sở đã được khởi tạo vào thời điểm hàm tạo lớp dẫn xuất của bạn có thể chạy. Bạn có thể gán chúng, nếu bạn có quyền truy cập, hoặc gọi bộ thiết lập cho chúng, hoặc bạn có thể cung cấp giá trị cho chúng cho phương thức khởi tạo lớp cơ sở, nếu có. Một điều bạn không thể làm trong lớp đã phát triển là khởi tạo chúng.
Marquis of Lorne,

Câu trả lời:


143

Bạn không thể khởi tạo avà đăng bnhập Bvì họ không phải là thành viên của B. Họ là thành viên của A, do đó chỉ Acó thể khởi tạo chúng. Bạn có thể đặt chúng ở chế độ công khai, sau đó thực hiện chuyển nhượng B, nhưng đó không phải là một tùy chọn được khuyến nghị vì nó sẽ phá hủy tính năng đóng gói. Thay vào đó, hãy tạo một phương thức khởi tạo Ađể cho phép B(hoặc bất kỳ lớp con nào của A) khởi tạo chúng:

class A 
{
protected:
    A(int a, int b) : a(a), b(b) {} // Accessible to derived classes
    // Change "protected" to "public" to allow others to instantiate A.
private:
    int a, b; // Keep these variables private in A
};

class B : public A 
{
public:
    B() : A(0, 0) // Calls A's constructor, initializing a and b in A to 0.
    {
    } 
};

32
trong khi ví dụ của bạn là đúng, giải thích của bạn là sai. Không phải là bạn không thể khởi tạo abvào B::B()vì chúng ở chế độ riêng tư. Bạn không thể khởi tạo chúng vì chúng không phải là thành viên của class B. Nếu bạn đặt chúng ở chế độ công khai hoặc được bảo vệ, bạn có thể chỉ định chúng trong phần nội dung của B::B().
R Samuel Klatchko

2
Ngoài ra, giải pháp của bạn làm cho lớp A không phải là tổng hợp, điều này có thể quan trọng, vì vậy nó phải được đề cập.
Gene Bushuyev

1
@R Samuel Klatchko: Điểm tốt. Khi tôi viết câu trả lời, ban đầu tôi nhập "Bạn không thể truy cập ab..." và thay đổi thành "Bạn không thể khởi tạo ..." mà không đảm bảo phần còn lại của câu có ý nghĩa. Đã chỉnh sửa bài đăng.
In silico

1
@Gene Bushuyev: Lớp trong mã gốc trong câu hỏi không phải là một tập hợp (có những thành viên riêng tư không tĩnh)
David Rodríguez - dribeas

@David - đúng, đó là lỗi của người dùng và tôi đang cố gắng đạt được ý định của người dùng, bỏ qua sự hời hợt.
Gene Bushuyev

26

Bỏ qua một thực tế rằng chúng private, vì ablà thành viên của A, chúng được khởi tạo bởi các hàm tạo của A', không phải bởi các hàm tạo của một số lớp khác (dẫn xuất hay không).

Thử:

class A
{
    int a, b;

protected: // or public:
    A(int a, int b): a(a), b(b) {}
};

class B : public A
{
    B() : A(0, 0) {}
};

7

Bằng cách nào đó, không ai liệt kê cách đơn giản nhất:

class A
{
public:
    int a, b;
};

class B : public A
{
    B()
    {
        a = 0;
        b = 0;
    }

};

Bạn không thể truy cập các thành viên cơ sở trong danh sách trình khởi tạo, nhưng chính phương thức khởi tạo, cũng như bất kỳ phương thức thành viên nào khác, có thể truy cập publicprotectedcác thành viên của lớp cơ sở.


1
Đẹp. Có bất kỳ hạn chế nào khi làm theo cách này?
Wander3r

2
@SaileshD: có thể có, nếu bạn đang khởi tạo một đối tượng bằng một hàm tạo đắt tiền. Đầu tiên nó sẽ được khởi tạo mặc định khi phiên bản của Bđược cấp phát, sau đó nó sẽ được gán bên trong hàm tạo Bcủa. Nhưng tôi cũng nghĩ rằng trình biên dịch vẫn có thể tối ưu hóa điều này.
Violet Giraffe,

1
Bên trong class Achúng ta không thể dựa vào abđược khởi tạo. class C : public AVí dụ: bất kỳ triển khai nào của có thể quên gọi a=0;và để lại trạng thái achưa khởi tạo.
Sparkofska

@Sparkofska, rất đúng. Tốt nhất là khởi tạo mặc định các trường tại chỗ khi khai báo chúng ( class A { int a = 0;};) hoặc trong hàm tạo của lớp cơ sở. Các lớp con vẫn có thể khởi tạo lại chúng trong phương thức khởi tạo của chúng khi cần thiết.
Violet Giraffe

1
@ Wander3r Một nhược điểm khác là không phải tất cả các lớp đều có toán tử gán. Một số chỉ có thể được xây dựng, nhưng không được chỉ định. Vậy thì bạn đã hoàn thành ...
Martin Pecka

2
# include<stdio.h>
# include<iostream>
# include<conio.h>

using namespace std;

class Base{
    public:
        Base(int i, float f, double d): i(i), f(f), d(d)
        {
        }
    virtual void Show()=0;
    protected:
        int i;
        float f;
        double d;
};


class Derived: public Base{
    public:
        Derived(int i, float f, double d): Base( i, f, d)
        {
        }
        void Show()
        {
            cout<< "int i = "<<i<<endl<<"float f = "<<f<<endl <<"double d = "<<d<<endl;
        }
};

int main(){
    Base * b = new Derived(10, 1.2, 3.89);
    b->Show();
    return 0;
}

Đây là một ví dụ hoạt động trong trường hợp bạn muốn khởi tạo các thành viên dữ liệu lớp Cơ sở có trong đối tượng lớp Derived, trong khi bạn muốn đẩy các giá trị này giao tiếp thông qua lệnh gọi phương thức khởi tạo lớp Derived.


1

Mặc dù điều này hữu ích trong một số trường hợp hiếm hoi (nếu không phải như vậy, ngôn ngữ sẽ cho phép nó trực tiếp), hãy xem thành ngữ Cơ sở từ Thành viên . Nó không phải là một giải pháp miễn phí về mã, bạn sẽ phải thêm một lớp kế thừa bổ sung, nhưng nó sẽ hoàn thành công việc. Để tránh mã viết sẵn, bạn có thể sử dụng triển khai của boost


0

Tại sao bạn không làm được? Bởi vì ngôn ngữ không cho phép bạn khởi tạo danh sách khởi tạo 'các thành viên trong lớp dẫn xuất' của lớp cơ sở.

Làm thế nào bạn có thể hoàn thành việc này? Như thế này:

class A
{
public:
    A(int a, int b) : a_(a), b_(b) {};
    int a_, b_;
};

class B : public A
{
public:
    B() : A(0,0) 
    {
    }
};

-1

Nếu bạn không chỉ định khả năng hiển thị cho một thành viên trong lớp, nó sẽ mặc định là "riêng tư". Bạn nên đặt các thành viên của mình ở chế độ riêng tư hoặc được bảo vệ nếu bạn muốn truy cập họ trong một lớp con.


-1

Các lớp tổng hợp, như A trong ví dụ (*) của bạn, phải có các thành viên của chúng là công khai và không có hàm tạo do người dùng xác định. Chúng được phức tạp hóa với danh sách trình khởi tạo, ví dụ A a {0,0};hoặc trong trường hợp của bạn B() : A({0,0}){}. Các thành viên của lớp tổng hợp cơ sở không thể được khởi tạo riêng lẻ trong phương thức khởi tạo của lớp dẫn xuất.

(*) Nói một cách chính xác, như đã đề cập một cách chính xác, bản gốc class Akhông phải là tổng hợp do các thành viên riêng tư không tĩnh

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.