无论你现在走到哪里,只要你不停止前进,你都正在创造你的未来。(ง •_•)ง
目录
类的继承
概述
什么是继承?
两个类之间具备is a关系,那么我们可以设置成继承 例如:猴子是动物
有了继承关系,父类的资源,子类可直接继承(继承属性和函数)
同时,子类可以派生出自己的属性和函数
好处:减少代码量,提升复用性和扩展性
继承的语法:
class 子类:继承方式 父类
{
//拥有父类的资源
//同时可派生出自己的资源
}
继承方式
继承方式: public:公共 protected:保护 private: 私有
继承方式为公共(public)时 基类继承到派生类的成员访问权限不变 基类的私有成员派生类无权访问
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son: public Base
{
public:
void func()
{
m_A; //可访问
m_B; //可访问 类内可访问保护权限
m_C; //不可访问 子类无法访问私有权限
}
};
void test_01()
{
Son s;
s.m_A; //可访问
s.m_B; //不可访问 类外不可访问保护权限
s.m_C; //不可访问 子类无法访问私有权限
}
继承方式为保护(protected)时 基类继承到派生类的成员公共权限变为保护权限其它访问权限不变 基类的私有成员派生类无权访问
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son: protected Base
{
public:
void func()
{
m_A; //可访问 但变为保护权限
m_B; //可访问 类内可访问保护权限
m_C; //不可访问 子类无法访问私有权限
}
};
void test_01()
{
Son s;
s.m_A; //不可访问 子类继承方式为protected 所以父类的公共权限变为保护权限 类外不可访问
s.m_B; //不可访问 类外不可访问保护权限
s.m_C; //不可访问 子类无法访问私有权限
}
继承方式为私有(private)时 基类继承到派生类的成员公共权限和保护权限变为私有权限私有访问权限不变 基类的私有成员派生类无权访问
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son: private Base
{
public:
void func()
{
m_A; //可访问 但变为私有权限
m_B; //可访问 但变为私有权限
m_C; //不可访问 子类无法访问私有权限
}
};
void test_01()
{
Son s;
s.m_A; //不可访问 子类继承方式为private 所以父类的公共权限变为私有权限 类外不可访问
s.m_B; //不可访问 子类继承方式为private 所以父类的公共权限变为私有权限 类外不可访问
s.m_C; //不可访问 子类无法访问私有权限
}
注意: 当子类继承方式为私有时 子类的子类继承了爷爷类的成员 但是所有成员全部为私有权限 无法访问
继承中的对象模型
子类继承父类的私有成员时 虽然无法访问 但是还是会继承下来 (同样占用空间)
class Base
{
public:
int m_A;
protected:
int m_B;
private:
int m_C;
};
class Son: public Base
{
};
void test_01()
{
Son s;
cout<<"sizeof(Son) = "<<sizeof(s)<<endl; //12
}
输出为:12 (证明父类的私有成员也被继承下来 只不过是无权访问)
继承中的析构和构造函数
思考:当子类继承父类后 创建子类对象的时候 构造函数是怎么创建的呐?
先构造父类在构造子类 析构函数和构造函数相反
class Base
{
public:
Base()
{
cout<<"父类的构造函数"<<endl;
}
~Base()
{
cout<<"父类的析构函数"<<endl;
}
};
class Son: public Base
{
public:
Son()
{
cout<<"子类的构造函数"<<endl;
}
~Son()
{
cout<<"子类的析构函数"<<endl;
}
};
void test_01()
{
Son s;
}
思考:当子类中有一个x类类型的成员 构造函数和析构函数又是什么顺序呐?
先构造父类再构造x类成员最后构造子类本身的构造函数 析构函数和构造函数 相反
class A
{
public:
A()
{
cout<<"A的构造函数"<<endl;
}
~A()
{
cout<<"A的析构函数"<<endl;
}
};
class Base
{
public:
Base()
{
cout<<"父类的构造函数"<<endl;
}
~Base()
{
cout<<"父类的析构函数"<<endl;
}
};
class Son: public Base
{
public:
Son()
{
cout<<"子类的构造函数"<<endl;
}
~Son()
{
cout<<"子类的析构函数"<<endl;
}
A m_a;
};
继承中的重名成员处理
非静态成员和非静态成员函数的重名成员处理
思考:当子类继承父类 父类成员有和子类成员重名的变量或函数 调用怎么调用呐?
定义一个子类对象 使用该对象来调用或使用重名变量或函数 使用的为子类的
当想使用父类的成员时 必须添加作用域 例如: 子类对象.(父类名::成员变量或函数)
class Base
{
public:
int m_A;
Base()
{
m_A = 1;
}
void func()
{
cout<<"父类的func()函数的调用"<<endl;
}
void func(int x)
{
cout<<"父类的func(int )函数的调用"<<endl;
}
};
class Son:public Base
{
public:
int m_A;
Son()
{
m_A = 2;
}
void func()
{
cout<<"子类的func()函数的调用"<<endl;
}
};
void test_01()
{
Son s;
s.func(); //调用子类的成员函数
s.Base::func(); //调用父类的成员函数
//s.func(1); 不可直接调用 因为会被覆盖
s.Base::func(1); //调用父类的成员函数 void func(int)
cout<<"s.m_A = "<<s.m_A<<endl; //调用子类成员
cout<<"s.Base::m_A = "<<s.Base::m_A<<endl; //调用父类的成员
}
注意:子类和父类重名后 会把父类的重名成员隐藏(并非重写) 父类同名成员函数的函数重载也会被隐藏当想调用父类成员 添加父类作用域即可
静态成员的重名处理
注意:静态成员在类内定义 类外初始化 静态成员不属于任何一个类对象
访问静态成员成员方法: 1.使用类对象名 2.使用类名
class Base
{
public:
static int m_A;
};
int Base:: m_A = 1;
class Son:public Base
{
public:
static int m_A;
};
int Son:: m_A = 2;
void test_01()
{
//利用子类对象访问:
Son s;
cout<<"s.m_A = "<<s.m_A<<endl;
cout<<"s.Base::m_A = "<<s.Base::m_A<<endl;
//利用类名访问
cout<<"Son::m_A = "<<Son::m_A<<endl;
cout<<"Son::Base::m_A = "<<Son::Base::m_A<<endl;
}
静态成员函数的重名处理
注意:静态成员函数只能调用静态成员变量 静态成员函数不属于任何一个类对象
访问静态成员函数方法: 1.使用类对象名 2.使用类名
class Base
{
public:
static void func()
{
//m_A = 0; //错误静态函数只可以调用静态成员变量
cout<<"父类func()静态函数"<<endl;
}
int m_A;
};
class Son:public Base
{
public:
static void func()
{
cout<<"子类func()静态函数"<<endl;
}
};
void test_01()
{
//创建类对象访问
Son s;
s.func();
s.Base::func();
//使用类名访问
Son::func();
Son::Base::func(); //利用子类类名访问父类静态成员函数
}
静态成员函数和非静态成员函数一样 :子类和父类重名后 会把父类的重名成员隐藏(并非重写) 父类同名成员函数的函数重载也会被隐藏当想调用父类成员 添加父类作用域即可
多继承基本语法(不建议使用)
多继承语法: class 类名: 继承方式 类名, 继承方式 类名
class A: public B, public C{};
class A
{
public:
A()
{
m_A = 10;
}
int m_A;
};
class B
{
public:
B()
{
m_B = 20;
}
int m_B;
};
class C: public A, public B
{
public:
int m_C;
int m_D;
};
void test_01()
{
C c;
cout<<c.m_A<<endl; //正常打印
cout<<c.m_B<<endl;
}
多继承的注意事项
当两个父类成员变量重名时 需要添加作用域
class A
{
public:
A()
{
m_A = 10;
}
int m_A;
};
class B
{
public:
B()
{
m_A = 20;
}
int m_A;
};
class C: public A, public B
{
public:
int m_C;
int m_D;
};
void test_01()
{
C c;
//cout<<c.m_A<<endl; //error 继承成员重名 需要添加作用域
cout<<c.A::m_A<<endl;
cout<<c.B::m_A<<endl;
}
多继承-菱形继承
class Animal
{
public:
int m_Age;
};
class Sheep:public Animal{};
class Tuo: public Animal{};
class SheepTuo: public Sheep,public Tuo
{
};
void test_01()
{
SheepTuo s;
//cout<<s.m_Age<<endl; //错误 因为是棱形循环 产生了二义性 需要输出要加作用域
s.Sheep::m_Age = 10;
s.Tuo::m_Age = 20;
cout<<s.Sheep::m_Age<<endl;
cout<<s.Tuo::m_Age<<endl;
}
当产生这种菱形循环后 可以定义虚继承
虚继承 :子类继承父类 当继承为虚继承时 父类的成员变量会变成虚基类指针 指向的是虚基类表 表中写的是父类指针的偏移量 该指针通过偏移就会找到唯一的一个变量位置
虚继承语法: class 类名 : virtual 继承类型 基类名
class Animal
{
public:
int m_Age;
};
class Sheep:virtual public Animal{}; //虚继承
class Tuo: virtual public Animal{};
class SheepTuo: public Sheep,public Tuo
{};
void test_01()
{
SheepTuo s;
//cout<<s.m_Age<<endl; //错误 因为是棱形循环 产生了二义性 需要输出要加作用域
s.Sheep::m_Age = 10;
s.Tuo::m_Age = 20;
cout<<"s.Sheep = "<<s.Sheep::m_Age<<endl;
cout<<"s.Tuo = "<<s.Tuo::m_Age<<endl;
cout<<"s.m_Age = "<<s.m_Age<<endl;
}
虚继承的底层原理会在下一篇多态中和虚函数一起讲解
本文章连载 建议收藏 一起进步😀