1.继承权限
public(公有访问) | protected(保护访问) | private(私有访问) | |
public(公有继承) | 继承后变成public(公有访问) | 继承后变成protected(保护访问) | 继承后不可访问 |
protected(保护继承) | 继承后变成protected(保护访问) | 继承后变成protected(保护访问) | 继承后不可访问 |
private(私有继承) | 继承后变成private(私有访问) | 继承后变成private(私有访问) | 继承后不可访问 |
注:protected和private的区别:
protected:在派生类中可以访问,类外不可访问
private:派生类和类外均不可访问
2.继承定义格式
3.同名隐藏
定义:基类,派生类某一个成员变量同名,或者某一个成员函数同名(只要函数名一样,不管参数列表,返回值是否一样),通过派生类调用该成员变量或成员函数时,只会调用派生类的,基类的看不到,这种现象称为同名隐藏。
#include <iostream>
#include <windows.h>
using namespace std;
class Base
{
public:
int _pub;
void Fun1()
{
cout<<"Base::void Fun1()"<<endl;
}
void Fun2(int)
{
cout<<"Base::void Fun2(int)"<<endl;
}
int Fun3()
{
cout<<"Base::int Fun3()"<<endl;
return 0;
}
};
class Derived:public Base
{
public:
int _pub;
void Fun1()
{
cout<<"Derived::Fun"<<endl;
}
void Fun2()//与Base中Fun2相比不带参数
{
cout<<"Derived::void Fun2(int)"<<endl;
}
void Fun3()//与Base中Fun3相比返回值不同
{
cout<<"Derived::void Fun3()"<<endl;
}
};
int main()
{
Derived d;
Base b;
b._pub=1;
d._pub=2;
cout<<d._pub<<endl;
d.Fun1();
d.Fun2();
d.Fun3();
system("pause");
return 0;
}
结果:
4.对象模型
测试代码:
#include <iostream>
#include <windows.h>
using namespace std;
class Base
{
public:
int _pub;
int _pro;
int _pri;
};
class Derived:public Base
{
public:
int _pub1;
};
int main()
{
Derived d;
Base b;
b._pub=1;
b._pro=2;
b._pri=3;
d._pub1=4;
d._pub=5;
d._pro=6;
d._pri=7;
system("pause");
return 0;
}
总结:基类的对象模型是按照声明次序的。派生类是先是基类的,然后是自己的(按照声明顺序排列)。
5.派生类对象
class Base
{
public:
Base()
{
cout<<"Base::Base()"<<endl;
}
~Base()
{
cout<<"Base::~Base()"<<endl;
}
private:
int _pub;
int _pro;
int _pri;
};
class Derived:public Base
{
public:
Derived()
{
cout<<"Derived::Derived()"<<endl;
}
~Derived()
{
cout<<"Derived::~Derived()"<<endl;
}
private:
int _pub1;
};
定义一个派生类对象
Derived d;
d的调用顺序为:Derived()->~Derived()
实际执行顺序:Base() ->Derived()-> ~Derived()->~Base()
小结:
(1)基类有缺省构造函数,派生类无构造函数,系统会给派生类自动合成
(2)基类无构造函数,派生类无构造函数,系统不会给基类,派生类自动合成
(3)基类构造函数有参数,派生类必须定义构造函数,并在初始化列表显示调用基类的构造函数
6.赋值兼容规则-----public继承
#include <iostream>
#include <windows.h>
using namespace std;
class Base
{
public:
int _b;
};
class Derived:public Base
{
public:
int _d;
};
int main()
{
Base b;
Derived d;
b=d;
d=b;
Base &b1=d;
Base *p=&d;
Derived &d=b;
Derived * p1=&b;
}
小结:
(1)子类对象可以赋值给父类对象。原理就像是切片/切割一样。
(2)父类对象不能赋值给子类对象。
(3)父类的指针/引用可以指向子类对象。
(4)子类的指针/引用不能指向父类对象。
7.友元函数&Static&继承
友元函数属于类外函数,所以不能继承。
static修饰的成员可以继承,但是,无论多少派生类,只有一份Static修饰的成员。
8.多继承
如: 有class A,class B,
class C:public A,public B
C类的父类有两个,便成C类为多继承。
注:继承多个类时,每个类的继承权限不能省略,否则,class默认的继承权限是private。
9.菱形继承
#include <iostream>
#include <windows.h>
using namespace std;
class B
{
public:
int _b;
};
class C1:public B
{
public:
int _c1;
};
class C2:public B
{
public:
int _c2;
};
class D:public C1,public C2
{
public:
int _d;
};
int main()
{
D d;
d.C1::_b;
d._c1;
d.C2::_b;
d._c2;
d._d ;
system("pause");
return 0;
}
注:D类对象访问_b时,需要加上作用域,不然会产生二义性。因为C1,C2类各有一个_b,D类对象访问时,不知要访问哪个。
10.虚继承---解决菱形继承的二义性和数据冗余的问题
继承权限前面加上virtual关键字。
例如:
class A
{
public:
int _a;
};
class B:virtual public A
{
public:
int _b;
};
int main()
{
B b;
b._a=1;
b._b=2;
return 0;
}
小结:
(1)虚继承中,在程序编译时,偏移量的表格已经生成。在调用构造函数时,只是将地址存入。
(2)虚继承中,A类若是无构造函数,B类自动合成构造函数。(将指向偏移量的地址存入对象的前四个字节)。
(3)虚继承与普通继承的区别在于,调用构造函数前,先PUSH 1,以此来判断是否为虚继承,若是,便执行(2)中所说。
(4)虚继承中,基类的成员变量只存在一份,所以当派生类对象调用基类对象时,不会存在二义性问题,因此,虚继承可以解决菱形继承中的二义性问题和数据冗余的问题。
用虚继承解决菱形继承中二义性问题:
#include <iostream>
#include <windows.h>
using namespace std;
class B
{
public:
int _b;
};
class C1:virtual public B
{
public:
int _c1;
};
class C2:virtual public B
{
public:
int _c2;
};
class D:public C1,public C2
{
public:
int _d;
};
int main()
{
D d;
d._b=1;
d._c1=2;
d._c2=3;
d._d =4;
system("pause");
return 0;
}
注意: 是C1和C2虚继承B类,所以只在这前面加virtual关键词。
下面举一个在不同位置加VIRTUAL关键词,看会引起什么不同
#include <iostream>
#include <windows.h>
using namespace std;
class B
{
public:
int _b;
};
class C1: public B
{
public:
int _c1;
};
class C2: public B
{
public:
int _c2;
};
class D:virtual public C1,virtual public C2
{
public:
int _d;
};
int main()
{
D d;
d.C1::_b=1;
d._c1=2;
d.C2::_b=5;
d._c2=3;
d._d =4;
system("pause");
return 0;
}
上述的这种情况,并没有解决菱形二义性的问题。