在基类构造器中调用虚方法需谨慎

最近,在基类的构造函数中调用虚方法时,发现了一个问题。先把问题重现如下:

    class Program
    {
        static void Main(string[] args)
        {
            var c = new Chinese(18);
            Console.ReadKey();
        }
    }

    public class People
    {

        public int Age { get; protected set; }

        protected People()
        {
            this.Say();
        }

        public virtual void Say()
        {
            Console.WriteLine(string.Format("基类说我今年{0}岁了", Age));
        }
    }

    public class Chinese : People
    {

        public Chinese(int age)
        {
            this.Age = age;
        }

        public override void Say()
        {
            Console.WriteLine(string.Format("子类说我今年{0}岁了",Age));
        }
    }

运行以上的代码,结果会是以下两种情况之一吗?
基类说我今年18岁了
子类说我今年18岁了

都不是!而是:

为什么?
→调用子类构造函数new Chinese(18)
→先调用父类无参构造函数,并调用虚方法Say
→发现虚方法Say有重载,就调用子类ChineseSay的重载,但此时子类Chinese还没有对Age属性初始化,此时的Age属性值为默认值0

如何避免呢?
--去掉在基类构造函数中调用虚方法的代码
--用子类实例调用重载方法

    class Program
    {
        static void Main(string[] args)
        {
            var c = new Chinese(18);
            c.Say();
            Console.ReadKey();
        }
    }

    public class People
    {

        public int Age { get; protected set; }

        protected People()
        {
        }

        public virtual void Say()
        {
            Console.WriteLine(string.Format("基类说我今年{0}岁了", Age));
        }
    }

    public class Chinese : People
    {

        public Chinese(int age)
        {
            this.Age = age;
        }

        public override void Say()
        {
            Console.WriteLine(string.Format("子类说我今年{0}岁了",Age));
        }
    }


总结:当在基类构造函数中调用虚方法需谨慎,因为当父类调用虚方法重载的时候,子类还没有被初始化,可能会涉及到一些成员还未被初始化。

时间: 2024-10-05 09:06:06

在基类构造器中调用虚方法需谨慎的相关文章

关于Java中基类构造器的调用问题

在<Java编程思想>第7章复用类中有这样一段话,值得深思.当子类继承了父类时,就涉及到了基类和导出类(子类)这两个类.从外部来看,导出类就像是一个与基类具有相同接口的新类,或许还会有一些额外的方法和域.但继承并不只是复制基类的接口.当创建一个导出类对象时,该对象包含了一个基类的子对象,这个子对象与你用基类直接创建的对象是一样的,二者区别在于,后者来自于外部,而基类的子对象是被包裹在导出类对象内部. 这就引发出了一个很重要的问题,对基类子对象的正确初始化也是至关重要的(我们可能在子类的使用基类

避免在构造函数中调用虚方法(Do not call overridable methods in constructors)

CLR中说道,不要在构造函数中调用虚方法,原因是假如被实例化的类型重写了虚方法,就会执行派生类型对虚方法的实现.但在这个时候,尚未完成对继承层次结构中所有字段的初始化.所以,调用虚方法会导致不可预测的行为.归根结底,这是由于调虚方法时,直到运行时之前,都不会选择执行该方法的实际类型. 在MSDN中,也给我我们详细的提示和范例. https://msdn.microsoft.com/en-us/library/ms182331.aspx 那我们就亲手来测试一下,新建两个类,Perople类,Chi

C#使用基类的引用 and 虚方法和覆写方法

结论:使用基类的引用,访问派生类对象时,得到的是基类的成员. 虚方法和覆写方法

EC笔记,第二部分:9.不在构造、析构函数中调用虚函数

9.不在构造.析构函数中调用虚函数 1.在构造函数和析构函数中调用虚函数会产生什么结果呢? #include <iostream> using namespace std; class cls1{ public: cls1(){ newMake(); }; ~cls1(){ deleteIt(); }; virtual void newMake(){ cout<<"cls1 make"<<endl; } virtual void deleteIt()

5 在C#中如何调用基类构造器

//基类 using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace MVATwentyQuestions { class Test { public string _name = ""; public Test(string name) { _name = name; } public virt

基类中的虚方法到底有什么作用?

只有基类的方法加上关键字virtual后才可以被override,从而实现面向对象最重要的特征--多态性,即基类可以使用派生类的方法. C#中指出:普通的方法重载:指的是类中两个以上的方法(包括隐藏的,继承而来的方法)取的名字相同,只要使用的参数类型或者参数个数不同,编译器便知道在何种情况下应该调用哪个方法.   而在派生类中重新定义此虚函数时要求的是:方法名称.返回值类型.参数表中的参数个数.类型.顺序都必须与基类中的虚函数完全一致. 简单一点说就是子类中override的方法能够覆盖积累中的

(转)Android中的基类—抽取出来公共的方法

在Android中,一般来说一个应用会存在几十个页面,并且一个应用一般也会使用一个特定的主题,其中的页面的风格也是一致的,并且页面中的动画效果.页面的切换效果等也应该保持同样的风格,那么就需要一个基类,来完成页面的基础设置,这就是所谓的基类. 界面统一管理原因 当应用中的界面达到一定数量级时,如几十个界面,便于项目管理 提高界面的处理速度 避免由于多Activity导致的问题 界面统一管理的基础 当应用中的界面达到一定数量级时,为了能够让用户更容易操作上手,产品设计时会将界面显示风格进行统一.

Android中的基类—抽取出来公共的方法

在Android中,一般来说一个应用会存在几十个页面,并且一个应用一般也会使用一个特定的主题,其中的页面的风格也是一致的,并且页面中的动画效果.页面的切换效果等也应该保持同样的风格,那么就需要一个基类,来完成页面的基础设置,这就是所谓的基类. 界面统一管理原因 当应用中的界面达到一定数量级时,如几十个界面,便于项目管理 提高界面的处理速度 避免由于多Activity导致的问题 界面统一管理的基础 当应用中的界面达到一定数量级时,为了能够让用户更容易操作上手,产品设计时会将界面显示风格进行统一.

(C++)浅谈多态基类析构函数声明为虚函数

主要内容: 1.C++类继承中的构造函数和析构函数 2.C++多态性中的静态绑定和动态绑定 3.C++多态性中析构函数声明为虚函数 1.C++类继承中的构造函数和析构函数 在C++的类继承中, 建立对象时,首先调用基类的构造函数,然后在调用下一个派生类的构造函数,依次类推: 析构对象时,其顺序正好与构造相反: 具体参考文章:http://www.cnblogs.com/AndyJee/p/4575385.html 2.C++多态性中的静态绑定和动态绑定 对象的静态类型:对象在声明是采用的类型,在