一个经典的问题(构造函数调用+抽象类+间接继承抽象类)

本文深入探讨了在间接继承抽象类的情况下,如何确定对象调用的方法是来自父类还是抽象类自身。通过具体示例,解释了在不同继承层次下方法调用的逻辑。

经典案例
案例1:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace ConsoleApplication1
{
    abstract class Test1
    {
        public int Num1 { get; private set; }
        public int Num2 { get; private set; }
        public Test1(int num1,int num2)
        {
            Num1=num1;
            Num2=num2;
        }
        public abstract int Sum();
    }

    class Test2:Test1
    {
        private int num3;
        public Test2(int num1, int num2, int num3):base(num1,num2)
        {
            Num3 = Sum();//问题:初始化Num3的时候调用的是Test3的方法而不是 Test2的?为什么
        }
        public int Num3
        {
            get { return num3; }
            set { num3 = value; }
        }
        public override int Sum()
        {
            return ++Num3;
        }
    }

    class Test3:Test2
    {
       private int num4;
        public Test3(int num1, int num2, int num3,int num4):base(num1,num2,num3)
        {
            Num4 = num4;
        }
        public int Num4
        {
            get { return num4; }
            set { num4 = value; }
        }

        //public override int Sum()//因为是第三个类的对象,所以说对于方法的调用都是在 第三个类里,如果第三个类里的方法没有重写,那么会继承第2个的方法,调用第二个类的方法
        //{
        //    return Num4+base.Sum();
        //}

    }

    class P
    {
        static void Main()
        {
            Test3 onetest3 = new Test3(1,2,3,4);
        }
        
    }
}

问题:

间接继承抽象类,重写方法是 父类的方法还是抽象类的方法?

答:间接继承抽象类的类 重写方法是父类的方法,“近水楼台先得月”!

注:
间接继承抽象类,重写方法是 父类的方法的,
直接继承抽象类,对于抽象类的方法那叫实现


答案:
如果没有注释Test3的Sum方法:
1.对于直接继承抽象类,必须实现  抽象类的抽象方法,如Test2
2.因为对象是在Test3建立的,而且Test3重写了父类Test2的方法,所以必须用本类的Sum方法。

如果把Test3的Sum方法注释:
1.对于直接继承抽象类,必须实现  抽象类的抽象方法,如Test2
2.对于间接继承抽象类的,没有必要一定实现  抽象类的抽象方法,如Test3
3.对于间接继承抽象类的类,如果没有重写抽象类方法,隐式调用父类的实现抽象类方法,如Test3调用了Test2的Sum()方法

在C++中,当一个派生类继承一个抽象类时,派生类的构造函数需要负责初始化从基类继承而来的成员。抽象类通常包含纯虚函数,不能直接实例化,但其派生类可以继承并实现这些纯虚函数。对于继承抽象类的成员变量,其初始化方式与普通类成员变量的初始化方式一致,但需要注意构造函数的初始化顺序和方式。 抽象类的成员变量在派生类中被视为普通基类成员的一部分,因此在派生类构造函数中,可以通过初始化列表来调用基类的构造函数,从而完成对基类成员的初始化。例如: ```cpp class Base { public: Base(int value) : data(value) {} virtual void print() = 0; // 纯虚函数 protected: int data; }; class Derived : public Base { public: Derived(int value) : Base(value) {} // 通过初始化列表调用基类构造函数 void print() override { std::cout << "Data: " << data << std::endl; } }; ``` 在上述代码中,`Derived`类的构造函数通过初始化列表调用了`Base`类的构造函数,并将值传递给基类的成员变量`data`。这种方式确保了基类成员在派生类对象构造时就被正确初始化[^4]。 如果抽象类中包含`const`或引用类型的成员变量,则这些成员必须在初始化列表中进行初始化,因为它们在构造函数体内无法被赋值。例如: ```cpp class Base { public: Base(int& value) : ref(value) {} protected: const int constData = 10; int& ref; }; class Derived : public Base { public: Derived(int& value) : Base(value) {} }; ``` 在此示例中,`Base`类包含一个`const int`类型的成员`constData`和一个`int&`类型的引用成员`ref`。由于`const`成员和引用成员必须在声明后立即初始化,因此它们的初始化必须在构造函数的初始化列表中完成[^2]。 此外,需要注意的是,基类的初始化顺序与派生类构造函数初始化列表中的顺序无关,而是按照基类在派生类中声明的顺序进行初始化。如果初始化列表中存在多个基类成员或成员对象,它们将按照在类中声明的顺序依次初始化。例如: ```cpp class Base { public: Base(int i) : m_i(i) {} protected: int m_i; }; class Derived : public Base { public: Derived(int j) : Base(j), m_j(j) {} private: int m_j; }; ``` 在这个例子中,尽管初始化列表中`Base(j)`出现在`m_j(j)`之前,但`m_j`是在`Base`构造函数执行之前被初始化的,因为`m_j`在类中先于`Base`部分声明[^4]。 ### 相关问题 1. 如何在C++中通过初始化列表正确初始化基类成员? 2. 为什么在派生类构造函数中必须使用初始化列表来初始化基类的`const`或引用成员? 3. C++中派生类构造函数的初始化顺序是如何决定的? 4. 抽象类中的纯虚函数是否会影响派生类构造函数的定义? 5. 在C++中,如何确保派生类构造函数正确初始化从多个基类继承的成员?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值