深入解析C++继承机制:从基础到高级特性
在C++编程中,继承是一种强大的机制,它允许我们通过创建新的类(派生类)来扩展已有的类(基类)的功能。本文将通过一系列示例代码,深入解析C++继承的多种特性,包括单继承、多继承、继承方式(public、protected、private)、构造与析构、同名成员处理、静态成员继承、菱形继承问题以及虚继承的解决方案。
一、单继承:扩展基类功能
单继承是C++中最基本的继承形式,派生类继承基类的成员,并可以添加新的成员或覆盖基类的成员。
示例代码解析
在继承.cpp
文件中,test1_1
函数展示了单继承的典型用法。baseclass1
是一个基类,定义了网站页面的通用部分(如头部、左侧边栏和底部)。派生类sonclass11
、sonclass12
和sonclass13
分别表示不同的子页面,它们继承了基类的通用部分,并添加了各自的内容。
cpp复制
sonclass11 java;
java.header();
java.left();
java.footer();
java.content();
运行结果:
复制
网站固定首页
网站固定左侧边栏
网站固定底页
子页1内容
关键点:
-
派生类继承了基类的公共成员(
header
、footer
、left
)。 -
派生类可以添加自己的成员(如
content
)。
二、继承方式:public、protected、private
C++提供了三种继承方式:public
、protected
和private
,它们决定了基类成员在派生类中的访问权限。
示例代码解析
在继承.cpp
文件中,test1_2
函数展示了三种继承方式的差异。
公共继承(public
)
cpp复制
class sonclass21 : public baseclass2
{
public:
void func()
{
m_a = 1; // 可访问
m_b = 2; // 可访问
// m_c = 3; // 不可访问,基类私有成员
}
};
-
基类的
public
和protected
成员在派生类中保持原有访问权限。 -
基类的
private
成员在派生类中不可访问。
保护继承(protected
)
cpp复制
class sonclass22 : protected baseclass2
{
public:
void func()
{
m_a = 1; // 可访问
m_b = 2; // 可访问
// m_c = 3; // 不可访问,基类私有成员
}
};
-
基类的
public
和protected
成员在派生类中变为protected
。 -
基类的
private
成员在派生类中不可访问。
私有继承(private
)
cpp复制
class sonclass23 : private baseclass2
{
public:
void func()
{
m_a = 1; // 可访问
m_b = 2; // 可访问
// m_c = 3; // 不可访问,基类私有成员
}
};
-
基类的
public
和protected
成员在派生类中变为private
。 -
基类的
private
成员在派生类中不可访问。
关键点:
-
不同的继承方式会影响基类成员在派生类中的访问权限。
-
私有继承通常用于实现细节的封装,而不是扩展功能。
三、构造与析构:继承中的构造顺序
在继承中,构造函数的调用顺序是:基类构造函数 → 派生类构造函数。析构函数的调用顺序是:派生类析构函数 → 基类析构函数。
示例代码解析
在继承.cpp
文件中,test1_4
函数展示了继承中的构造和析构顺序。
cpp复制
class baseclass4
{
public:
baseclass4()
{
cout << "构造base" << endl;
}
~baseclass4()
{
cout << "析造base" << endl;
}
};
class sonclass4 : public baseclass4
{
public:
sonclass4()
{
cout << "构造son" << endl;
}
~sonclass4()
{
cout << "析造son" << endl;
}
};
运行结果:
复制
构造base
构造son
子类继承父类,构造父类,构造子类,析构子类,析构父类
析造son
析造base
关键点:
-
构造函数的调用顺序是:基类 → 派生类。
-
析构函数的调用顺序是:派生类 → 基类。
四、同名成员处理:隐藏与覆盖
在继承中,如果派生类和基类有同名成员,派生类的成员会隐藏基类的成员。如果需要访问基类的同名成员,可以通过作用域解析运算符::
来实现。
示例代码解析
在继承.cpp
文件中,test1_5
函数展示了同名成员的处理方式。
cpp复制
class baseclass5
{
public:
int m_a = 0;
void func() const
{
cout << "base func()" << endl;
}
void func(int a) const
{
cout << "base func(int a)" << endl;
}
};
class sonclass5 : public baseclass5
{
public:
int m_a = 1;
void func() const
{
cout << "son func()" << endl;
}
};
运行结果:
复制
1
0
son func()
base func()
base func(int a)
关键点:
-
派生类的成员会隐藏基类的同名成员。
-
可以通过
基类名::成员名
来访问基类的同名成员。
五、静态成员继承:静态成员的特殊性
静态成员属于类,而不是类的对象。在继承中,静态成员的行为与其他成员略有不同。
示例代码解析
在继承.cpp
文件中,test1_6
函数展示了静态成员的继承特性。
cpp复制
class baseclass6
{
public:
static int m_a;
static void func()
{
m_a = 10;
cout << m_a << endl;
}
};
int baseclass6::m_a = 1;
class sonclass6 : public baseclass6
{
public:
static int m_a;
static void func()
{
m_a = 100;
cout << m_a << endl;
}
};
int sonclass6::m_a = 2;
运行结果:
复制
2
1
1
1
100
10
关键点:
-
静态成员可以通过类名直接访问。
-
派生类可以继承基类的静态成员,但可以通过覆盖来定义自己的静态成员。
六、多继承:复杂的继承关系
C++支持多继承,即一个类可以继承多个基类。然而,多继承可能会导致一些问题,如菱形继承问题。
示例代码解析
在继承.cpp
文件中,test1_7
函数展示了多继承的用法。
cpp复制
class baseclass7_1
{
public:
int m_a = 1;
};
class baseclass7_2
{
public:
int m_a = 2;
int m_b = 3;
};
class sonclass7 : public baseclass7_1, public baseclass7_2
{
public:
int m_b = 4;
int m_c = 5;
};
运行结果:
复制
1
2
3
4
5
建议不使用多继承,同名时比较混乱
关键点:
-
多继承可能导致同名成员的冲突。
-
建议尽量避免使用多继承,以减少复杂性和潜在的错误。
七、菱形继承问题及其解决方案:虚继承
菱形继承是多继承中常见的问题,它会导致派生类继承多份基类的相同数据。为了解决这个问题,C++引入了虚继承。
示例代码解析
在菱形继承.cpp
文件中,test1_8
函数展示了虚继承的用法。
cpp复制
class animal
{
public:
int m_age = 0;
};
class sheep : virtual public animal
{
public:
string m_color = "black";
};
class tuo : virtual public animal
{
public:
string m_sex = "man";
};
class sheeptuo : public sheep, public tuo
{
public:
int height = 0;
};
运行结果:
60 10 blue none
关键点