zoukankan      html  css  js  c++  java
  • c++, 派生类的构造函数和析构函数 , [ 以及operator=不能被继承 or Not的探讨]

     说明:文章中关于operator=实现的示例,从语法上是对的,但逻辑和习惯上都是错误的。

    参见另一篇专门探究operator=的文章:《c++,operator=》http://www.cnblogs.com/mylinux/p/4113266.html

    1.构造函数与析构函数不会被继承;[1]

      不是所有的函数都能自动地从基类继承到派生类中的。构造函数和析构函数是用来处理对象的创建和析构的,它们只知道对在它们的特殊层次的对象做什么。所以,在整个层次中的所有的构造函数和析构函数都必须被调用,也就是说,构造函数和析构函数不能被继承。
      另外,operator= 也不能被继承,因为它完成类似于构造函数的活动。//All overloaded operators except assignment (operator=) are inherited by derived classes.

    2.派生类的构函数被调用时,会先调用基类的其中一个构造函数,因为在派生类的构造函数中用了初始化表的方式调用了基类构造函数,默认不写时是调用了基类中可以不传参数的构造函数。

    3.在派生类对象释放时,先执行派生类析构函,再执行其基类析构函数。

    #include <iostream>
    using namespace std;
    #include <string>
    
    class Basic
    {
    public:
        string m_name;
        Basic();
        Basic(string name);
        Basic(Basic& bc);
    
        ~Basic();
    
        Basic& operator=(const Basic& bc) 
        {
            cout << "Basic::operator=()
    ";
            this->m_name = bc.m_name;
            return *this;
        }
    };
    Basic::Basic()
    {
        cout <<"Basic::Basic()"<<endl;
    }
    Basic::Basic(string name)
    {
        m_name = name ;
        cout <<"Basic::Basic(string)"<<"name:"<<m_name<<endl;
    }
    Basic::Basic(Basic &bc)
    {
        this->m_name = bc.m_name;
        cout <<"Basic::Basic(Basic&)"<<"name:"<<bc.m_name<<endl;
    }
    
    Basic::~Basic()
    {
        cout<<"this is "<<m_name << "Basic::~Basic()"<<endl;;
    }
    
    
    class Derived:public Basic
    {
    public:
        int m_dx;
        Derived();
        Derived(string name);//m_name
        ~Derived();
    
        void show();
    };
    Derived::Derived()
    {
        cout <<"Derived::Derived()"<<endl;
    }
    Derived::Derived(string name)
        :Basic(name)
    {
        cout <<"Derived::Derived(string)"<<"name:"<<m_name<<endl;
    }
    Derived::~Derived()
    {
        cout <<"this is "<<m_name <<"Derived::~Derived()"<<endl;;
    }
    void Derived::show()
    {
        cout<<"name: "<<m_name<<endl;
    }
    void test()
    {
        Derived dc1("dc1");
        cout<<""<<endl;
    
        Derived dc2("dc2");//m_bx
        cout<<""<<endl;
    
    
        dc1 = dc2 ;//Basic::operator=() 调用了基类的operator= ,并正确地对Derived::m_name进行了赋值。 看起来是operator=被继承了?分析见下5补充。
    cout
    <<"next is dc1.show(): "; dc1.show(); cout<<""<<endl; } int main() { test(); while(1); } /** Basic::Basic(string)name:dc1 Derived::Derived(string)name:dc1 Basic::Basic(string)name:dc2 //生成派生类对象时,先调用基类的构造函数 Derived::Derived(string)name:dc2 //在调用自身的构造函数 Basic::operator=() //调用了基类的operator= ,并正确地对Derived::m_name进行了赋值。
                //测试时,假如把基类的operator=实现为空函数,那么Derived对象也不能对Derived::m_name进行重新赋值。除非再手动实现
    Derived的operator=
                //operator= 只有一个,Drived中如果手动实现了,将会覆盖基类的=。就是说,不会执行基类的operator=。
    next is dc1.show(): name: dc2 this is dc2Derived::~Derived() //析构和构造的调用顺序刚好相反。先调用自身的析构函数,再调用基类的析构函数。 
    this is dc2Basic::~Basic()
    this is dc2Derived::~Derived()//在一个函数体中,先实现的对象后释放。
    this is dc2Basic::~Basic()
    *
    */

    4.派生类构造函数首行的写法:

    class Basic
    {
    public:
        int m_number;
        string m_name ;
        char m_sex;//'m' 'w'
        Basic(int n ,string name , char s);
    };
    Basic::Basic(int n,string name ,char s)
        :m_number(n)
        ,m_name(name)
        ,m_sex(s)
    {
    //     this->m_name = name;
    //     this->m_number = n;
    //     this->m_sex = s;
    }
    
    class Derived:public Basic
    {
    public:
        int m_age;
        string m_addr;
        Derived(int n,string name,char s,int a, string addr);
    };
    Derived::Derived(int n,string name,char s,int a, string addr)
        :Basic(n,name,s)
        ,m_age(a)
        ,m_addr(addr)
    {
    
    }

    5.  operator=不能被继承 or Not的探讨

    关于operator=的说法比较有争议,以下面的实验结果为准。

    (1) operator= 不能被继承[2](个人认为,拷贝形式的除外,否则不能解释上面代码的打印结果以及下面代码的实验结果)。

        //MSDN: All overloaded operators except assignment (operator=) are inherited by derived classes.

        [如果非要说不能够继承,那也会调用基类中的拷贝式operator=。]

    (2)通过使用“using 某类::operator某运算符”语句,就可以继承基类中的运算符了。[3]

    //下面的实验说明 
    // 1. 一般的operator=不会被继承。
    // 2.通过使用“using 某类::operator某运算符”语句,就可以“继承”基类中的运算符了。而如果没有加上该语句,编译会出错。
    // 3.msdn和C++的国际标准《ISO/IEC 14882》都说了operator=不能被继承,但是通过在vs2010中的实验,在派生类没有自定义operator=的情况下,派生了会执行基类中的拷贝式operator=。
    // 【对于派生类增加的变量赋值,系统默认给定,具体例子见下面的补充说明】
    #include <iostream> using namespace std ; class A1 { public: int operator=(int a) { return 8; } int operator+(int a) { return 9; } int operator=(A1 &a) { cout<<"operator=(A1 &a)"<<endl; return 0; } }; class B1 : public A1 { public: int operator-(int a) { return 7; } #if 1 using A1::operator= ; #endif }; void test_common() { B1 v; cout << (v + 2) << endl; // OK, print 9 cout << (v - 2) << endl; // OK, print 7 cout << (v = 2) << endl; // 如果class B1加了 using A1::operator= ; ,强行“继承”了A1的operator=,则结果为8. // 否则: error C2679: 二进制“=”: 没有找到接受“int”类型的右操作数的运算符(或没有可接受的转换)。 } void test_copy() { B1 v2; B1 v3; v2 = v3 ;//打印效果: operator=(A1 &a) //并且拷贝性质的operator=,从效果上看,能够被继承。 //拷贝性质的operator=,如果基类未对其显式地定义,始终还是能被编译器隐式的定义。 } int main() { test_common(); test_copy(); while(1); return 0; }

    关于operator是否继承的问题,下面补充一下:

    //下面的实验说明 
    // 1. 一般的operator=不会被继承。
    // 2.通过使用“using 某类::operator某运算符”语句,就可以“继承”基类中的运算符了。而如果没有加上该语句,编译会出错。
    // 3.msdn和C++的国际标准《ISO/IEC 14882》都说了operator=不能被继承,
    // 但是通过在vs2010中的实验说明:虽然说不能够继承,但是也会调用执行基类中的拷贝式operator=。
    // 4.那么执行了基类的operator=,但是基类operator=没有办法对派生类增加的成员变量赋值,剩下的操作就由系统默认给定(按对象的内存地址依次复制)。
    //   
    #include <iostream>
    using namespace std ;
    /////////////////////////
    class A1
    {
    public:
        int operator=(int a)
        {
            return a;
        }
        A1& operator=(const A1 &a);
        A1(int val);
        int GetVal();
    private:
        int m_x;
    };
    
    A1::A1(int val):m_x(val)
    {
        
    }
    
    int A1::GetVal()
    {
        return this->m_x;
    }
    
    A1& A1::operator=(const A1 &a) 
    {
        cout<<"operator=(A1 &a)"<<endl;
        this->m_x = a.m_x *10 ;//注意*10是为了看效果
        return *this;
    }
    /////////////////////////////////
    class B:public  A1
    {    
    private:
        int m_b;
    public:
        B(int vala,int valb);
    
        int GetValB()
        {
            return m_b;
        }
    };
    
    B::B(int vala,int valb):A1(vala),m_b(valb)
    {
    
    }
    /////////////////////////////////////////////////////
    void test_inheritance()
    {
        B  v(11,44);
        cout<<"get value:"<<v.GetVal()<<endl;//11
    
        B v2(22,33);
        cout<<"get value:"<<v2.GetVal()<<endl;//22
        cout<<"get valueB:"<<v2.GetValB()<<endl;//33
    
        v2 = v  ; // operator=(A1 &a) 
        cout<<"get value:"<<v2.GetVal()<<endl;// 110   执行了基类的operator=
        cout<<"get valueB:"<<v2.GetValB()<<endl;// 44  这里是系统默认生产的。
        // 打印operator=(A1 &a) 说明给派生类赋值时,执行了 基类operator= 。
        // 虽然执行了基类的operator=,但是基类operator=没有办法对派生类增加的成员变量赋值,剩下的操作就由系统默认给定(按对象的内存地址依次复制)。
        // [基类operator=为什么在这里会执行,是不是和构造函数的相同,必然会执行呢?答案是否定的,当B自定义了operator=之后,不会执行基类中的operator=。]
    }
    int main()
    {        
        test_inheritance();
    
        while(1);
        return 0;
    }

    参考:

    1.构造与析构函数与 operator=不能被继承
      http://www.cnblogs.com/findumars/p/3695340.html

    2.为什么C++赋值运算符重载函数不能被继承?
      http://www.eetop.cn/blog/html/11/317611-14436.html 

    3.C++重载运算符的继承

      http://blog.csdn.net/raodotcong/article/details/5501181

  • 相关阅读:
    (C/C++学习笔记) 十四. 动态分配
    (C/C++学习笔记) 十三. 引用
    (C/C++学习笔记) 十二. 指针
    (C/C++学习笔记) 十一. 数组
    (C/C++学习笔记) 十. 函数
    (C/C++学习笔记) 九. 变量的存储类型
    (C/C++学习笔记) 八. 程序控制语句
    并发编程之多进程
    网络编程之Socket
    异常处理
  • 原文地址:https://www.cnblogs.com/mylinux/p/4094808.html
Copyright © 2011-2022 走看看