基本知识
继承是面向对象三大特性之一(三大特性:封装,继承和派生,多态)。
当一些类之间存在特殊的关系:下级别成员拥有上一级的共性,还有自己的特性。这时可以用继承,减少重复代码。
例子
学生类和工人类都有人(名字、年龄、性别)这个类的共性,而它们都有自己的特性:学生有成绩、工人有水平。这样可以让学生类和工人类继承人类:
class Person
{
public:
Person(){}
~Person(){}
private:
char* m_name;
char m_sex;
int m_age;
};
//class 子类 :继承权限 父类
class Student:public Person
{
public:
Student(){}
~Student(){}
private:
float m_score;
};
class Worker:public Person
{
public:
Worker(){}
~Worker(){}
private:
int m_level;
};
子类又叫派生类,父类又叫基类。
继承三步
1.将父类中除了构造和析构之外的内容全盘接收
2.子类改写了父类的方法
3.子类添加自己特有的属性或方法
继承方式:
继承方式有三种:公共继承public、保护继承protected、私有继承private、
注意:
父类私有private会被继承但是子类谁都不能访问
保护protected在子类中可以访问,外界不可访问
继承时的权限是更改父类的public或protected(越变范围越小),外部访问受影响
继承方式影响如下:
public:权限不变 protected:公有变保护、private:保护&公有变私有
例子
公有私有保护继承:
class A
{
public:
int m_i;
protected:
int m_j;
private:
int m_k;
};
class B:public A
{//在内部只能用父类的public&protected
void printf() {
m_i = 10;
m_j = 20;
//m_k = 30;//错误:父类中的私有成员子类不能使用
}
};
class C:protected A
{
void printf()
{
m_i = 10;
m_j = 20;
//m_k = 30;//error
}
};
class D :private A
{
void printf() {
m_i = 10;
m_j = 20;
// m_k = 30;//error
}
};
int main()
{
cout << sizeof(B)<<endl<<sizeof(C)<<endl<<sizeof(D)<< endl;
//12 12 12 把m_i,m_j,m_k都继承了
B b;
C c;
D d;
b.m_i = 10;//在外界b只能用public
//C继承A的时候,将public属性变成protected了,外界不能用它任何东西了
//D继承A的时候,将public和protected属性变成private了,外界不能访问
}
私有和保护的区别
多层继承:
B通过私有方式继承A,C通过公有方式继承B,C无法使用A内的任何成员
B通过保护方式继承A,C通过公有方式继承B,C可以使用A内的保护成员
构造和析构顺序
如果调用子类,则先调用父类构造,再调用子类构造(先有爸爸再有儿子),析构的顺序与构造顺序相反。
class A
{
public:
A() { cout << "~A" << endl; }
~A() { cout << "~A" << endl; }
};
//:继承权限 基类
class B :public A
{
public:
B() { cout << "B" << endl; }
~B() { cout << "~B" << endl; }
};
int main()
{
B b;
}
//子类没有权力对继承下来的成员构造或销毁
同名成员处理
当子类与父类出现同名的成员,通过子类对象访问子类或父类中的同名数据:
访问子类同名成员,直接访问;
访问父类同名成员,加作用域。
class A
{
public:
A():m_i(10) {}
void fun() { cout << "A的fun" << endl; }
int m_i;
};
class B :public A
{
public:
B() :m_i(20) {}
void fun() { cout << "B的fun" << endl; }
int m_i;
};
int main()
{
B b;
cout << b.m_i << endl << b.A::m_i << endl;
b.fun();
b.A::fun();
}
如果子类中出现和父类同名的成员函数,子类的同名成员会隐藏掉父类中所有同名成员函数(重载的也会被隐藏),如果想访问到父类中被隐藏的同名成员函数,需要加作用域。
继承同名静态成员处理
静态成员在静态区中,不占用类的空间,所有类共享——静态成员函数没有this指针
静态比全局的优点:
1.只有类对象可以访问,可以隐藏,全局不行
2.避免与其他全局变量重复
注意:
可以直接在类中初始化
不能在类初始化列表中初始化
在外部声明时不需要再用static
使用同名静态成员与普通成员一样,通过对象和类名都可以访问。
class A
{
public:
static int m_i=10;
};
class B :public A
{
public:
static int m_i=20;
};
int main()
{
//访问A类中m_i——10
cout<<A::m_i<<endl;
//访问B类中m_i——20
cout<<B::m_i<<endl;
//通过类名方式访问A类中m_i——10
cout<<B::A::m_i<<endl;
//访问b对象中m_i——20
B b;
cout<<b.m_i<<endl;
}
多继承语法
一个类继承多个类
class 子类: 继承方式 父类1, 继承方式 父类2…
多继承可能引发父类中有同名的父类(二义性),这时需要使用作用域。
不建议使用多继承!
class B1
{
public:
B1(){m_i = 100;};
int m_i;
};
class B2
{
public:
B2(){m_i = 200;};
int m_i;
};
class Son:public B1,public B2
{
public:
Son(){m_a = 300;m_b = 400;};
int m_a;
int m_b;
};
int main()
{
Son s;
cout<<s.B1::m_i<<" "<<s.B2::m_i<<endl;//100 200
}
菱形继承
菱形继承(钻石继承):两个派生类继承同一个基类,又有某个类同时继承两个派生类
菱形继承产生的问题:
1.父亲和伯伯都继承了爷爷的东西,当孙子使用时,会产生二义性
2.孙子继承了两份同样的数据(来自爷爷的),但这数据只需要一份
解决方法:
1.二义性加作用域区分
2.虚继承解决两份同样数据
class A
{
public:
A(int i = 0) :m_a(i) {}
int m_a;
};
class B:public A
{
public:
B(int b = 0) :m_b(b) { }
int m_b;
};
class C:public A
{
public:
C(int c = 0) :m_c(c) { }
int m_c;
};
class D :public B, public C
{
public:
D(int d = 0) :m_d(d) {}
int m_d;
};
void main()
{
cout << sizeof(D) << endl;//5*4==20,将A的数据继承了两份
}
虚继承
在继承之前加上关键字virtual变为虚继承;
最先的类叫虚基类。
class B:virtual public A
虚继承是继承了一个虚基类指针(vbptr),指向虚基类表,表中存储着虚类的地址(偏移量),使用时在虚基类表中找这个数据。