目录
继承是C++中面向对象编程的核心特性之一,允许派生类继承基类的成员变量和成员函数,从而实现代码的重用和层次化设计。
继承方式与访问控制:
public:共有权限,类内类外都能够进行访问。
protected:保护权限,类内、友元和派生类能够进行访问。
private:私有权限,类内、友元能够进行访问。
public继承:基类的public、protected和private成员在派生类中都保持原有权限。
protected继承:基类的public和protected在派生类中为protected权限,private在派生类中还是保持原有权限。
private继承:基类的public、protected在派生类中都为private权限,private成员不可访问。
如果不想让一个类被继承,使用final关键字声明该类不能被继承。
访问权限:
示例:
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
A():A_public(0),A_protected(0),A_private(0){}
A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){}
public:
int A_public;
protected:
int A_protected;
private:
int A_private;
};
int main() {
A a;
std::cout << a.A_public << std::endl;
std::cout << a.A_protected << std::endl;
std::cout << a.A_private << std::endl;
return 0;
}
运行结果:

可以知道如果想直接在类外直接访问protected和private成员是无法访问的。
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
A():A_public(0),A_protected(0),A_private(0){}
A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){}
void Get() {
std::cout << A_protected << " " << A_private << std::endl;
}
public:
int A_public;
protected:
int A_protected;
private:
int A_private;
};
int main() {
A a(1,2,3);
std::cout << a.A_public << std::endl;
a.Get();
return 0;
}
运行结果:

通过成员函数来进行访问类中的protected和private成员。
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
friend void Get(const A& other);
public:
A():A_public(0),A_protected(0),A_private(0){}
A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){}
public:
int A_public;
protected:
int A_protected;
private:
int A_private;
};
void Get(const A& other) {
std::cout << other.A_public << " " << other.A_protected << " " << other.A_private << std::endl;
}
int main() {
A a(1,2,3);
std::cout << a.A_public << std::endl;
Get(a);
return 0;
}
运行结果:

通过friend关键字在类中声明一个友元函数,在类外进行定义之后,来对类内的protected和private进行一个访问,public成员也能够对其进行访问。
继承:
当一个派生类继承基类时,派生类继承基类的所有成员变量和成员函数,但是根据继承的方式,对于基类的成员变量或者成员函数在派生类中有一个访问权限的转换。需要注意的是无论是public、protected或者private继承,只会影响基类的public和protected成员在派生类的权限,而基类的private成员在派生类中不可见,不可以直接对其进行访问,只能通过基类的方法对其进行访问。
示例:
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
//friend void Get(const A& other);
public:
A():A_public(0),A_protected(0),A_private(0){
std::cout << "类A的无参构造" << std::endl;
}
A(int A_public,int A_protected,int A_private):A_public(A_public),A_protected(A_protected),A_private(A_private){
std::cout << "类A的有参构造" << std::endl;
}
public:
int A_public;
protected:
int A_protected;
private:
int A_private;
};
class B :public A {
};
class C :protected A {
};
class D :private A {
};
int main() {
B b;
C c;
D d;
std::cout << b.A_public << std::endl;
//std::cout << c.A_public << std::endl;
//std::cout << d.A_public << std::endl;
return 0;
}
运行结果:

类B、C和D分别是public、protected和private继承类A,同时三个类都未提供构造函数。但是通过运行结果可以知道都调用了类A的无参构造函数。这里需要注意的是,并不是派生类继承了基类的构造函数,派生类并不会继承基类的任何构造函数,而是派生类中没有定义构造函数,编译器会自动生成一个默认构造函数,该函数自动调用了基类的默认构造函数。其中类B是public继承所以能够访问基类A的public成员,而类C、D分别是protected和private继承,基类A的public成员分别转换成protected权限和private权限,所以不能够直接进行访问。
构造和析构顺序:
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
friend void Get(const A& other);
public:
A():A_private(0){
std::cout << "类A的无参构造" << std::endl;
}
A(int A_private):A_private(A_private){
std::cout << "类A的有参构造" << std::endl;
}
~A() {
std::cout << "类A的析构函数" << std::endl;
}
private:
int A_private;
};
void Get(const A& other) {
std::cout << other.A_private << std::endl;
}
class C {
public:
C() {
std::cout << "类C的无参构造" << std::endl;
}
~C() {
std::cout << "类C的析构函数" << std::endl;
}
};
class B :public A {
public:
B():B_private(0){
std::cout << "类B的无参构造" << std::endl;
}
B(int B_private):B_private(B_private){
std::cout << "类B的有参构造" << std::endl;
}
~B() {
std::cout << "类B的析构函数" << std::endl;
}
private:
int B_private;
C c;
};
int main() {
B b(3);
return 0;
}
运行结果:

类B继承类A,然后在类B中定义一个成员变量c,c的类型是类C。那么通过上述运行结果,可以看出,先对基类A进行构造,然后再对派生类B的成员变量进行构造,这里需要注意的是,是按照成员变量的生命顺序来进行构造,最后才是对派生类本身类B进行构造。
析构的顺序,从运行结果不难看出,析构顺序与构造顺序相反,先析构派生类,然后对派生类的成员变量按照生命顺序从后往前进行析构,最后对基类进行析构。
多态:
多态是面向对象编程的三大特性之一,指统一操作作用于不同对象时产生不同的行为。
示例:
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
A():A_private(0){
std::cout << "类A的无参构造" << std::endl;
}
A(int A_private):A_private(A_private){
std::cout << "类A的有参构造" << std::endl;
}
~A() {
std::cout << "类A的析构函数" << std::endl;
}
void Get() {
std::cout << "类A的Get()函数" << std::endl;
}
private:
int A_private;
};
class B :public A {
public:
B():B_private(0){
std::cout << "类B的无参构造" << std::endl;
}
B(int B_private):B_private(B_private){
std::cout << "类B的有参构造" << std::endl;
}
~B() {
std::cout << "类B的析构函数" << std::endl;
}
/*void Get() {
std::cout << "类B的Get()函数" << std::endl;
}*/
private:
int B_private;
};
int main() {
B b;
b.Get();
return 0;
}
运行结果:

类B继承类A,创建一个类B然后进行调用Get()函数,通过运行结果可以知道,调用了基类中的Get()成员函数。那么如果在派生类B中也进行定义一个成员函数Get(),那么会调用基类的还是派生类的Get()函数?
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
A():A_private(0){
std::cout << "类A的无参构造" << std::endl;
}
A(int A_private):A_private(A_private){
std::cout << "类A的有参构造" << std::endl;
}
~A() {
std::cout << "类A的析构函数" << std::endl;
}
void Get() {
std::cout << "类A的Get()函数" << std::endl;
}
private:
int A_private;
};
class B :public A {
public:
B():B_private(0){
std::cout << "类B的无参构造" << std::endl;
}
B(int B_private):B_private(B_private){
std::cout << "类B的有参构造" << std::endl;
}
~B() {
std::cout << "类B的析构函数" << std::endl;
}
void Get() {
std::cout << "类B的Get()函数" << std::endl;
}
private:
int B_private;
};
int main() {
B b;
b.Get();
return 0;
}
运行结果:

从结果可以看出,在派生类定义了一个和基类一样的成员函数,那么再通过派生类进行调用该函数,不再调用基类的Get()成员函数,而是派生类中的Get()成员函数。
非虚函数重写:
派生类中定义与基类中同名的非虚函数时,会隐藏掉基类的同名函数实现。非虚函数采用的时静态绑定,再编译时就确定调用。当派生类对象直接调用该函数时,编译器会优先匹配派生类版本,而忽略基类的实现。如果想在派生类中进行使用基类的同名函数需要使用基类的作用域来进行调用。
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
A():A_private(0){
std::cout << "类A的无参构造" << std::endl;
}
A(int A_private):A_private(A_private){
std::cout << "类A的有参构造" << std::endl;
}
~A() {
std::cout << "类A的析构函数" << std::endl;
}
void Get() {
std::cout << "类A的Get()函数" << std::endl;
}
private:
int A_private;
};
class B :public A {
public:
B():B_private(0){
std::cout << "类B的无参构造" << std::endl;
}
B(int B_private):B_private(B_private){
std::cout << "类B的有参构造" << std::endl;
}
~B() {
std::cout << "类B的析构函数" << std::endl;
}
void Get() {
std::cout << "类B的Get()函数" << std::endl;
}
void Get_A() {
A::Get();
}
private:
int B_private;
};
int main() {
B b;
b.Get();
b.Get_A();
return 0;
}
运行结果:

虚函数:
虚函数是C++实现运行多态的关键机制,通过在基类中使用virtual关键字进行声明,允许派生类重写该函数实现。与上述非虚函数重写不同的是,当通过基类指针或者引用调用虚函数时,程序会根据实际对象类型动态决定调用。包含虚函数的类在编译时会生成一个虚函数表,存储该类的所有虚函数的地址。虚函数表是连续的内存结构,派生类会继承基类的虚函数表,并替换重写函数的地址,未重写的函数保留基类的实现。通过基类的指针或者引用调用虚函数时,编译器会生成一个虚函数表指针,通过该指针来进行定位函数地址之后进行动态绑定。
override关键字显示声明重写意图,编译器会对重写的函数进行检查是否书写正确。
基类可以使用final关键字防止派生类进一步重写该函数。
示例:
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
A():A_private(0){
std::cout << "类A的无参构造" << std::endl;
}
A(int A_private):A_private(A_private){
std::cout << "类A的有参构造" << std::endl;
}
~A() {
std::cout << "类A的析构函数" << std::endl;
}
void Get() {
std::cout << "类A的Get()函数" << std::endl;
}
virtual void func() {
std::cout << "类A的func()函数" << std::endl;
}
private:
int A_private;
};
class B :public A {
public:
B():B_private(0){
std::cout << "类B的无参构造" << std::endl;
}
B(int B_private):B_private(B_private){
std::cout << "类B的有参构造" << std::endl;
}
~B() {
std::cout << "类B的析构函数" << std::endl;
}
void Get() {
std::cout << "类B的Get()函数" << std::endl;
}
void Get_A() {
A::Get();
}
void func()override {
std::cout << "类B的func()函数" << std::endl;
}
private:
int B_private;
};
int main() {
A* p = new B();
p->Get();
p->func();
delete p;
return 0;
}
运行结果:

通过基类指针指向派生类对象的地址,如果是非虚函数调用的是基类的同名成员函数,但是如果是虚函数,那么调用的是派生类中的重写的虚函数。
从上述结果能够看出析构时,并没有调用派生类的析构函数,只调用了基类的析构函数。因为编译器根据指针的类型查找析构函数,因为是基类指针,所以直接调用基类析构跳过了派生类析构,导致基类释放而派生类未释放,就只会释放派生类中包含的基类,从而导致整体对象内存释放不完整。所以为了避免这种问题可以将基类的析构函数写为虚析构。
也就是基类指针指向派生类时,对基类指针进行释放时,如果积累的析构函数不是虚函数,只会调用基类的析构函数,导致派生类部分未被正确清理。
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
A():A_private(0){
std::cout << "类A的无参构造" << std::endl;
}
A(int A_private):A_private(A_private){
std::cout << "类A的有参构造" << std::endl;
}
virtual ~A() {
std::cout << "类A的析构函数" << std::endl;
}
void Get() {
std::cout << "类A的Get()函数" << std::endl;
}
virtual void func() {
std::cout << "类A的func()函数" << std::endl;
}
private:
int A_private;
};
class B :public A {
public:
B():B_private(0){
std::cout << "类B的无参构造" << std::endl;
}
B(int B_private):B_private(B_private){
std::cout << "类B的有参构造" << std::endl;
}
~B() {
std::cout << "类B的析构函数" << std::endl;
}
void Get() {
std::cout << "类B的Get()函数" << std::endl;
}
void Get_A() {
A::Get();
}
void func()override {
std::cout << "类B的func()函数" << std::endl;
}
private:
int B_private;
};
int main() {
A* p = new B();
p->Get();
p->func();
delete p;
return 0;
}
运行结果:

纯虚函数:
纯虚函数通过"=0"将虚函数声明为纯虚函数,纯虚函数在派生类中必须实现该函数。
如果一个类包含至少一个纯虚函数,那么该类就称为抽象类,抽象类不能够对其进行实例化。
cpp
#include <iostream>
#include <string>
#include <thread>
class A {
public:
virtual ~A() {
std::cout << "类A的析构函数" << std::endl;
}
virtual void func() = 0 {
std::cout << "类A的func()函数" << std::endl;
}
};
class B :public A {
public:
B():B_private(0){
std::cout << "类B的无参构造" << std::endl;
}
B(int B_private):B_private(B_private){
std::cout << "类B的有参构造" << std::endl;
}
~B() {
std::cout << "类B的析构函数" << std::endl;
}
void func()override {
std::cout << "类B的func()函数" << std::endl;
}
private:
int B_private;
};
int main() {
A* p = new B();
p->func();
delete p;
return 0;
}
运行结果:
