继承

赋值兼容性原则:

class B:public A
B b;
A *p =&b;  //子类对象初始化父类指针
A &pp =b;  //子类对象初始化父类引用
A a = b;   //子类对象初始化父类对象
A a2;
a2 = b;   //子类对象赋值给父类对象

继承中的对象模型:

在子类对象构造时,在子类对象中需要调用基类构造函数来对继承而来的成员变量进行初始化;

在子类对象析构时,在子类对象中需要调用基类的析构函数来对继承而来的成员变量进行清理。

 

继承中构造析构:

构造:创建子类对象时,先调用父类构造函数

析构:先析构子类对象,再调用基类析构函数

原因:继承而来的父类私有成员,在子类中不可访问。

class A
{
    public:
        A(int _a):a(_a){}
    private:
        int a;
};

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

void main()
{
    B b;  //编译错误
}

因为在B类中,没有给出从A继承而来的a初始化方案,仅仅调用B的构造函数不能初始化a,所以编译出错。修改:

class A
{
    public:
        A(int _a):a(_a){}
    private:
        int a;
};

class B :public A
{
    public:
        B() : A(12) {b = 0;}
    private:
        int b;
};

void main()
{
    B b;
}
class A
{
    public:
        A() {}
    private:
        int a;
};

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

void main()
{
    B b;   //编译通过
}

对比上述三个例程代码:

在A中定义了带参的构造函数,那么A将不会生成默认的不带参构造函数了,所以在类B中,需要显示地调用A的构造函数来初始化继承过来的a(初始化列表方式)。对于在A中没有定义构造函数的情况(或是定义了不带参的构造函数)编译器在创建子类对象时,能够自动调用基类的构造函数。

 

继承遇到同名函数/变量:(隐藏)

派生类中函数或是成员变量与基类同名了,那么使用子类对象访问同名量,将隐藏基类的同名量;默认访问子类的同名量;

如果要使用子类对象访问基类的同名量,需要使用域作用符——::

class A
{
    public:
        A():{a=1;b=2;}
        void fun(){cout<<"A"<<endl;}
    private:
        int a;
        int b;
};

class B :public A
{
    public:
        B(){b = 0;}
        void fun(){cout<<"B"<<endl;}
    private:
        int a;
        int b;
};

void main()
{
    B b1;
    b1.fun();     //子类的
    b1.A::fun(); //基类的
}
void main()
{
    B b1;
    A* p = &b1;
    p->fun();    //基类的(隐藏看指针类型)
}
void main()
{
      B b1;
      A &p = b1;
      p.fun();          //基类的,隐藏看指针类型(引用也是指针)
}    

隐藏的特点:

发生在基类和派生类之间。

函数构成隐藏的两种情况:

函数名相同,参数不同,不管是否带有virtual修饰,都构成隐藏。

函数名相同,参数相同,基类函数不带virtual修饰,构成隐藏关系。

class A
{
    public:
        A(){}
        void fun(){cout<<"A"<<endl;}
        void abc(){cout<<"A"<<endl;}
        void atd(){cout<<"A"<<endl;}
    private:
        int a;
        int b;
        int c;
};


class B :public A
{
    public:
        B(){}
        void fun(){cout<<"B"<<endl;}
        void atd(int a){cout<<"B"<<endl;}
    private:
        int a;
        int b;
        int c;
};

class C :public A ,public B
{
    public:
        B(){}
    private:
        int a;
        int b;
        int c;
};

void main()
{
    B b1;
    b1.abc();    //abc函数从基类派生而来,且不为private,所以编译通过
    b1.fun();    //隐藏关系,调用B类的。
    
    A* a1 = &b1;
    a1->fun();    //调用A类的。
    
    b1.atd();    //由于从基类派生过来的被隐藏,所以不能识别,b1只能识别到有参的atd
}

 

 

继承遇到static:

基类静态成员,派生类同基类共享,访问受控制属性限制(基类private的静态成员,在子类中不可访问)

注意:为什么static变量一定要初始化?

static变量初始化的目的是为了给static分配空间。否则变量变得不可访问。

class A
{
    public:
        A(){a=1;}
        static int b;
        static void fun(){cout<<"A"<<endl;}
    private:
        int a;
};

int A::b = 0;

class B :public A
{
    public:
        B(){a = 0;}
        void fun_2()
        {
            fun();    //派生类中可访问
            b = 3;    //派生类中可访问
        }
    private:
        int a;
};

void main()
{
    B b1;
    b1.fun_2();    

    getchar();
}

 

多继承:

class C :public A , public B

多继承的二义性:

class A
class B1 :public A
class B2 :public A

class C :public B1 ,public B2

虚继承——解决多继承的二义性。

class A
class B1 :virtual public A
class B2 :virtual public A

class C :public B1 ,public B2

 

posted @ 2016-10-17 14:28  e-data  阅读(94)  评论(0)    收藏  举报