十年网站开发经验 + 多家企业客户 + 靠谱的建站团队
量身定制 + 运营维护+专业推广+无忧售后,网站问题一站解决
环境:
编译器:CLion2021.3;操作系统:macOS Ventura 13.0.1
继承是C++的三大基本特性之一。
一、继承的基本语法语法: class 子类 : 继承方式 父类
其中子类也称为派生类,父类也称为基类。派生类中的成员,包含两大部分:一类是从基类继承过来的,一类是自己增加的成员。从基类继承过过来的表现其共性,而新增的成员体现了其个性。
//公共页面
class BasePage {public:
void header() {cout<< "首页、公开课、登录、注册...(公共头部)"<< endl;
}
void footer() {cout<< "帮助中心、交流合作、站内地图...(公共底部)"<< endl;
}
void left() {cout<< "Java,Python,C++...(公共分类列表)"<< endl;
}
};
//Java页面
class Java : public BasePage {public:
void content() {cout<< "JAVA学科视频"<< endl;
}
};
//Python页面
class Python : public BasePage {public:
void content() {cout<< "Python学科视频"<< endl;
}
};
//C++页面
class CPP : public BasePage {public:
void content() {cout<< "C++学科视频"<< endl;
}
};
二、继承方式继承方式一共有三种:公共继承、保护继承、私有继承。
2.1 public继承class Base1 {public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
//公共继承
class Son1 : public Base1 {public:
void func() {m_A = 10; //可访问 public权限
m_B = 10; //可访问 protected权限
// m_C; //不可访问
}
};
void myClass() {Son1 s1;
s1.m_A; //ok,其他类只能访问到公共权限
// s1.m_B; //err,其他类只能访问到公共权限,m_B是protected,类外不可以访问
}
2.2 protected继承//保护继承
class Base2 {public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son2 : protected Base2 {public:
void func() {m_A; //可访问 protected权限
m_B; //可访问 protected权限
//m_C; //不可访问
}
};
void myClass2() {Son2 s;
//s.m_A; //不可访问,此时m_A变成了protected
}
2.3 private继承//私有继承
class Base3 {public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son3 : private Base3 {public:
void func() {m_A; //可访问 private权限
m_B; //可访问 private权限
//m_C; //不可访问
}
};
class GrandSon3 : public Son3 {public:
void func() {//Son3是私有继承,所以继承Son3的属性在GrandSon3中都无法访问到
// m_A;//err
// m_B;//err
// m_C;//err
}
};
2.4 继承规则三、继承中的对象模型父类中所有非静态成员属性都会被继承下去,私有成员属性只是访问不到,因为被编译器隐藏了,但是也被继承下去了。
class Base {public:
int m_A;
protected:
int m_B;
private:
int m_C; //私有成员只是被隐藏了,但是还是会继承下去
};
//公共继承
class Son : public Base {public:
int m_D;
};
void test01() {cout<< "sizeof Son = "<< sizeof(Son)<< endl;//16,说明private也被继承了
}
四、继承中的构造和析构顺序子类继承父类后,当创建子类对象,也会调用父类的构造函数,此时先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反。
class Base {public:
Base() {cout<< "Base构造函数!"<< endl;
}
~Base() {cout<< "Base析构函数!"<< endl;
}
};
class Son : public Base {public:
Son() {cout<< "Son构造函数!"<< endl;
}
~Son() {cout<< "Son析构函数!"<< endl;
}
};
void test01() {//继承中 先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反
Son s;
}
五、继承同名成员处理方式当子类与父类出现同名的成员,如何通过子类对象,访问到子类或父类中同名的数据呢?
对象.属性
对象.父类::属性
需要注意的一点是:当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数,否则无法访问。强调一下,只要函数名相同,其他的不管一不一样,都会被隐藏。
class Base {public:
int m_A;
public:
Base() {m_A = 100;
}
void func() {cout<< "Base - func()调用"<< endl;
}
void func(int a) {cout<< "Base - func(int a)调用"<< endl;
}
};
class Son : public Base {public:
int m_A;
public:
Son() {m_A = 200;
}
//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
void func() {cout<< "Son - func()调用"<< endl;
}
};
void test01() {Son s;
cout<< "Son下的m_A = "<< s.m_A<< endl;
cout<< "Base下的m_A = "<< s.Base::m_A<< endl;//加个作用域就可以访问父类的
s.func();
s.Base::func();
//子类中的同名成员函数会隐藏掉父类有所有同名成员函数,包括重载的,要想访问父类中被隐藏的同名成员函数的需要加作用域
// s.func(10);//err,父类中的有参函数被隐藏,而子类又没有有参函数,需要加作用域
s.Base::func(10);//ok
}
六、继承同名静态成员处理方式静态成员和非静态成员出现同名,处理方式一致,只不过有两种访问的方式:通过对象和通过类名。比非静态多了一个通过类名访问,这是静态成员本身的特性所导致的。
关于静态成员,可以参考「地表最强」C++核心编程(四)类和对象—对象初始化和清理第八点。
class Base {public:
static int m_A;
static void func() {cout<< "Base - static void func()"<< endl;
}
static void func(int a) {cout<< "Base - static void func(int a)"<< endl;
}
};
int Base::m_A = 100;
class Son : public Base {public:
static int m_A;
static void func() {cout<< "Son - static void func()"<< endl;
}
};
int Son::m_A = 200;
//同名成员属性
void test01() {//通过对象访问
cout<< "通过对象访问: "<< endl;
Son s;
cout<< "Son 下 m_A = "<< s.m_A<< endl;
cout<< "Base 下 m_A = "<< s.Base::m_A<< endl;
//通过类名访问
cout<< "通过类名访问: "<< endl;
cout<< "Son 下 m_A = "<< Son::m_A<< endl;
//第一个::代表通过类名访问,第二个::代表访问父类作用域下
cout<< "Base 下 m_A = "<< Son::Base::m_A<< endl;//这样通过子类
// cout<< "Base 下 m_A = "<< Base::m_A<< endl;//这是直接通过父类访问,也是对的
}
//同名成员函数
void test02() {//通过对象访问
cout<< "通过对象访问: "<< endl;
Son s;
s.func();
s.Base::func();
//通过类名访问
cout<< "通过类名访问: "<< endl;
Son::func();
Son::Base::func();
//出现同名,子类会隐藏掉父类中所有同名成员函数,需要加作作用域访问
Son::Base::func(100);
// Son::func(100);//err,父类中的同名函数被隐藏了
}
七、多继承语法C++允许一个类继承多个类,就是多继承。但多继承可能会引发从父类中继承多个同名成员,需要加作用域区分。C++实际开发中不建议用多继承。
语法: class 子类 : 继承方式 父类1 , 继承方式 父类2…
class Base1 {public:
int m_A;
public:
Base1() {m_A = 100;
}
};
class Base2 {public:
int m_A;
public:
Base2() {m_A = 200; //若是m_B则不会出问题,但是改为mA就会出现不明确
}
};
//语法:class 子类:继承方式 父类1 ,继承方式 父类2
class Son : public Base2, public Base1 {public:
int m_C;
int m_D;
public:
Son() {m_C = 300;
m_D = 400;
}
};
//多继承容易产生成员同名的情况,这是就要通过类名作用域来区分调用的是哪一个基类的成员
void test01() {Son s;
cout<< "sizeof Son = "<< sizeof(s)<< endl;
cout<< s.Base1::m_A<< endl;
cout<< s.Base2::m_A<< endl;
}
八、菱形继承和虚继承两个派生类继承同一个基类,又有某个类同时继承这两个派生类,这种继承被称为菱形继承,或者钻石继承。
菱形继承的问题:
1.羊继承了动物的数据,驼同样继承了动物的数据,当草泥马使用数据时,就会产生二义性。
2.草泥马继承自动物的数据继承了两份,但这个据我们只需要一份,这导致资源浪费而且毫无意义。
通过虚继承就可以解决菱形继承带来的问题。
语法: class 子类 : virtual 继承方式 父类
virtual关键字使得继承方式变成了虚继承,此时两个子类从同一父类那里继承来的是虚基类指针vbptr(virtual base pointer),这个指针指向了各自的vbtable(虚基类表),这个表中记录了一个偏移量,通过这个偏移量就可以找到需要的数据,这个数据只有一份。
class Animal {public:
int m_Age;
};
//虚继承,此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo : virtual public Animal {};
//羊驼类
class SheepTuo : public Sheep, public Tuo {};
void test01() {SheepTuo st;
//相同数据加作用域区分即可
st.Sheep::m_Age = 100;
cout<< "st.Sheep::m_Age = "<< st.Sheep::m_Age<< endl;//100
st.Tuo::m_Age = 200;//由于虚继承数据只有一份,操作这个会更改之前的赋值
cout<< "st.Sheep::m_Age = "<< st.Sheep::m_Age<< endl;//200,确实更改了
cout<< "st.Tuo::m_Age = "<< st.Tuo::m_Age<< endl;//200
cout<< "st.m_Age = "<< st.m_Age<< endl;//由于virtual,这种访问方式可以了,否则是错误的
}
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧