概述
继承就是再一个已有类的基础上建立一个新类,已有的类称基类或父类,新建立的类称为派生类和子类
特点
从已有类派生出新类后,可以在派生类内完成以下几种功能:
可以增加新的数据成员和成员函数
可以对基类的成员进行重定义
可以改变基类成员在派生类中的访问属性
一个派生类继承了所有的基类方法,但下列情况除外:
基类的构造函数、析构函数和拷贝构造函数。
基类的重载运算符。
基类的友元函数。
一个基类可以派生出多个派生类,一个派生类可以继承多个基类
派生类的声明
子类继承父类,子类拥有父类中全部成员变量和成员方法(除了构造和析构之外的成员方法),但是在子类中,继承的成员并不一定能直接访问,不同的继承方式会导致不同的访问权限
派生类的继承方式
继承方式有:private(默认),public,protected
总结:不管啥继承方式:父类中的私有数据在子类中不可见
派生类的构造函数
派生类要向基类初始化
1.子类会默认调用父类的无参构造
2.子类必须显式使用初始化列表调用父类的有参构造
派生类名(总参数列表):基类名(基类参数列表),子对象名1(参数列表){构造函数体;}
父类和子类的同名成员变量处理
1.当父类和子类成员变量同名时在子类就近原则选择本作用域的子类成员
2.如果在子类中必须使用父类中的同名成员必须加上作用域(父类::)
#include <iostream> class Parent { public: int value = 10; }; class Child : public Parent { public: int value = 20; void printValues() { std::cout << value << std::endl; // 输出:20 std::cout << Parent::value << std::endl; // 输出:10 } }; int main() { Child childObj; childObj.printValues(); return 0; }
父类和子类的同名成员函数处理
1.子类中的同名成员函数会覆盖父类中的同名成员函数
2.如果在子类中必须要调用父类的同名成员函数必须加作用域(父类::)
#include<iostream> using namespace std; class Base { public: void func(){ cout<<"父类中的void func"<<endl; } void func(int a){ cout<<"父类中的int func a = "<<a<<endl; } }; class Son :public Base { public: void func(){ cout<<"子类中的void func"<<endl; } void func(int a){ cout<<"子类中的int func a = "<<a<<endl; } }; int main() { Son ob; ob.func(); ob.func(10); ob.Base::func(); ob.Base::func(10); return 0; }
继承中的静态成员
特点:
静态成员可以被继承:子类会继承父类中定义的静态成员。这意味着子类可以直接访问并使用父类的静态成员变量和静态成员函数。
可以通过类名或对象来访问静态成员:对于继承的静态成员,可以使用类名或对象来访问。如果使用子类对象访问静态成员,将会优先访问子类自己的静态成员,而不是父类的静态成员。
静态成员可以被子类隐藏:如果子类中声明一个与父类静态成员同名的静态成员,那么将会隐藏父类的静态成员。这时,子类对象访问该成员时只能访问自己的隐藏静态成员。
例:
class Base { public: static int num; static int data; static void showData(); }; int Base::num=100; int Base::data=200; void Base::showData() { cout<<"父类中的data:"<<data<<endl; } class Son:public Base { public: static int data;//同名成员变量 static void showData(); }; int Son::data=300; void Son::showData() { cout<<"子类中的data:"<<data<<endl; } int main() { //从Base类中访问 cout<<Base::num<<endl; //Son中也拥有了这个静态数据,也可以从Son类中访问 cout<<Son::num<<endl; //父类和子类同名静态成员变量 //1.在子类中访问子类成员 cout<<Son::data<<endl; //2.在子类中访问父类成员 cout<<Son::Base::data<<endl;//加了个作用域而已 //父类和子类同名静态成员函数 //1.在子类中默认访问的是子类成员函数 Son::showData(); //2.在子类中访问父类成员函数 Son::Base::showData();//加了个作用域而已 return 0; }
继承的内层结构(了解)
总结
父类的public、protected、private都占用子类空间。虽然在子类中访问不到父类的private,但是依旧占用位置
案例:
#include<iostream>
using namespace std;
class Base
{
public:
int a;
protected:
int b;
private:
int c;
};
class Son :public Base
{
public:
int d;
int e;
};
int main()
{
cout << sizeof(Son) << endl;//20
return 0;
}
查看
运行后---在开始界面找到Developer Command Prompt for VS 2022
找到文件路径,并复制路径
命令行中输入
cl /d1 reportSingleClassLayoutSon test.cpp
父类的private,在子类中访问不到,但是依旧占用位置
继承中构造和析构函数调用顺序(2种情况)
case1:只有父类和子类
先执行基类的构造函数,随后执行派生类的构造函数
先执行派生类的析构函数,再执行基类的析构函数。
例:
class A { private: public: A(){ cout<<"A的构造函数"<<endl; } ~A(){ cout<<"A的析构函数"<<endl; } }; class B:public A { private: public: B(){ cout<<"B的构造函数"<<endl; } ~B(){ cout<<"B的析构函数"<<endl; } }; int main() { B b; return 0; }
case2:有三种:父类的构造和析构、对象的构造和析构、子类的构造和析构
先调用父类的构造函数,再调用对象的构造函数,最后调用子类的构造函数。
先调用子类的析构函数,再调用对象的析构函数,最后调用父类的析构函数。
例:
#include<iostream> using namespace std; class Other { public: Other() { cout<<"对象成员的构造函数"<<endl; } ~Other(){ cout<<"对象成员的析构函数"<<endl; } }; class Base { public: Base() { cout<<"父类的构造函数"<<endl; } ~Base() { cout<<"父类的析构函数"<<endl; } }; class Son:public Base { public: Son(){ cout<<"子类的无参构造"<<endl; } ~Son(){ cout<<"子类中的析构函数"<<endl; } Other ob;//对象成员 }; int main() { Son ob; return 0; }
图解
using关键字修改访问权限
利用using关键字可以改变基类成员再派生类中的访问权限;using只能修改基类中public和protected成员的访问权限。
class Base { public: void show(){ }; protected: int aa; double dd; }; class Person:public Base { public: using Base::aa;//将基类的protected成员变成public using Base::dd;//将基类的protected成员变成public private: using Base::show;//将基类的public成员变成private string name; }; int main() { Person *p = new Person(); p->aa = 12; p->dd = 12.3; p->show();//出错 delete p; return 0; }