解锁C++新姿势:组合与继承大揭秘

目录

一、C++ 的基石:组合与继承

二、组合:构建代码的积木

(一)组合的概念

(二)组合的实现细节

(三)组合的优势

三、继承:代码的传承与进化

(一)继承的定义与形式

(二)继承中的访问控制

(三)继承中的构造与析构

(四)重定义基类函数

四、组合 VS 继承:如何抉择

(一)“is - a” 与 “has - a”

(二)优缺点剖析

(三)适用场景对比

五、总结:掌握组合与继承,驾驭 C++ 编程


一、C++ 的基石:组合与继承

        在 C++ 的编程世界里,组合(Composition)与继承(Inheritance)就像是大厦的基石,支撑起面向对象编程(OOP)的宏伟架构。它们不仅是代码复用的关键手段,更是理解 C++ 语言特性和编写高效、可维护代码的核心概念。

        组合,简单来说,就是一个类中包含其他类的对象作为成员变量,体现了一种 “has - a”(有一个)的关系。比如,汽车类(Car)中包含引擎类(Engine)、轮胎类(Tire)的对象,因为一辆汽车 “有一个” 引擎和多个轮胎 。这种方式使得代码结构更加清晰,不同类的职责明确,通过组合不同的类,可以构建出复杂的系统。

        继承,则是一个新类(派生类,Derived Class)可以获取已有类(基类,Base Class)的属性和方法,表现为 “is - a”(是一个)的关系。例如,轿车类(Sedan)继承自汽车类(Car),轿车 “是一种” 汽车,它自然拥有汽车的基本属性,如轮子数量、发动机等,同时还可以有自己特有的属性和方法,像更舒适的座椅、高级的导航系统等。继承为代码复用提供了一种层次化的结构,使得相似功能的代码可以被集中管理和维护。

        组合和继承在 C++ 编程中无处不在,它们深刻影响着我们构建软件系统的方式。合理运用组合和继承,能够让我们的代码更加简洁、灵活和可扩展,极大地提高开发效率和代码质量。接下来,让我们深入探索这两个重要概念的细节、用法以及它们之间的区别和选择策略。

二、组合:构建代码的积木

(一)组合的概念

        组合,是一种将其他类的对象作为新类的数据成员的方式,它体现了一种 “has - a”(有一个)的关系 。这种关系就像是搭积木,我们把不同的积木(类对象)组合在一起,形成一个更复杂的结构(新类)。

        假设我们有一个表示有理数的Rational类,现在要构建一个表示复数的Complex类。复数由实部和虚部组成,而实部和虚部都可以用有理数来表示。这时,我们就可以在Complex类中组合两个Rational类的对象,分别表示实部和虚部。代码示例如下:

class Rational {

private:

int numerator; // 分子

int denominator; // 分母

public:

Rational(int num, int den) : numerator(num), denominator(den) {}

// 其他成员函数,如加法、减法等

};

class Complex {

private:

Rational realPart; // 实部

Rational imaginaryPart; // 虚部

public:

Complex(int realNum, int realDen, int imagNum, int imagDen)

: realPart(realNum, realDen), imaginaryPart(imagNum, imagDen) {}

// 其他成员函数,如复数加法、乘法等

};

        在这个例子中,Complex类 “有一个”Rational类对象作为实部,“有一个”Rational类对象作为虚部,这就是组合关系的体现。通过组合,Complex类可以利用Rational类已有的功能,而无需重新实现。

(二)组合的实现细节

        在使用组合时,有一个重要的实现细节需要注意,那就是对象成员的初始化。当一个类包含其他类的对象作为成员时,这些对象成员必须在初始化列表中进行初始化,而不能在构造函数体内进行赋值。这是因为在进入构造函数体之前,对象成员已经被默认构造了一次,如果在构造函数体内再进行赋值,就相当于进行了一次额外的操作,既降低了效率,又可能引发一些潜在的问题。

        比如,我们有一个A类和一个B类,B类中包含A类的对象作为成员:

class A {

public:

A(int value) : data(value) {

cout << "A constructor with value: " << data << endl;

}

private:

int data;

};

class B {

public:

// 正确的初始化方式,使用初始化列表

B(int aValue) : memberA(aValue) {

cout << "B constructor" << endl;

}

// 错误的方式,不能在构造函数体内赋值

// B(int aValue) {

// memberA = A(aValue); // 错误,memberA先默认构造,再赋值

// cout << "B constructor" << endl;

// }

private:

A memberA;

};

        在上述代码中,B类的构造函数通过初始化列表来初始化memberA,这样可以确保memberA在构造时就被正确初始化,避免了不必要的默认构造和赋值操作。

(三)组合的优势

        组合在 C++ 编程中具有诸多优势。它实现了代码的复用。通过组合已有的类,我们可以快速构建新的类,而无需重复编写已有的功能。就像搭积木一样,我们可以利用现有的积木块搭建出各种不同的结构,而不需要重新制造每一块积木。以std::queue和std::deque的关系为例,std::queue是通过组合std::deque实现的,std::queue利用了std::deque的接口来实现队列的功能,从而复用了std::deque的代码,避免了重复实现底层的数据结构和操作。

        组合有助于维持类的封装性。当我们使用组合时,被组合的类的内部细节对于组合类来说是隐藏的。组合类只需要通过被组合类的公共接口来使用其功能,而不需要了解其内部实现。这使得代码的维护和修改更加容易,因为我们可以独立地修改被组合类的内部实现,而不会影响到组合类。

        组合还可以提高代码的灵活性。我们可以在运行时动态地改变组合关系,通过更换不同的对象成员,实现不同的功能。比如,一个图形绘制类可以组合不同的形状类对象,在运行时根据用户的选择绘制不同的图形,这种灵活性是继承所难以提供的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

大雨淅淅

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值