组合介绍以及与继承对比
什么是组合
(1)composition,组合,就是在一个class内使用其他多个class的对象作为成员
(2)用class tree做案例讲解
(3)组合也是一种代码复用方法,本质也是结构体包含
#include <iostream>
#include <vector>
#include <string>
// Leaf 类
class Leaf {
public:
Leaf(const std::string& color) : color_(color) {
std::cout << "Leaf constructor called: " << color_ << std::endl;
}
~Leaf() {
std::cout << "Leaf destructor called: " << color_ << std::endl;
}
void display() const {
std::cout << "Leaf color: " << color_ << std::endl;
}
private:
std::string color_;
};
// Branch 类
class Branch {
public:
Branch(int length) : length_(length) {
std::cout << "Branch constructor called: " << length_ << " cm" << std::endl;
}
~Branch() {
std::cout << "Branch destructor called: " << length_ << " cm" << std::endl;
}
void display() const {
std::cout << "Branch length: " << length_ << " cm" << std::endl;
}
private:
int length_;
};
// Tree 类,包含 Leaf 和 Branch 对象
class Tree {
public:
Tree(const std::string& leafColor, int branchLength)
: leaf_(leafColor), branch_(branchLength) {
std::cout << "Tree constructor called" << std::endl;
}
~Tree() {
std::cout << "Tree destructor called" << std::endl;
}
void display() const {
leaf_.display();
branch_.display();
}
private:
Leaf leaf_;
Branch branch_;
};
int main() {
Tree tree("Green", 150);
tree.display();
return 0;
}
继承与组合的特点对比
(1)继承是a kind of(is a)关系,具有传递性,不具有对称性。
(2)组合是a part of(has a)的关系,
(3)继承是白盒复用。因为类继承允许我们根据自己的实现来覆盖重写父类的实现细节,父类的实现对于子类是可见的。
(4)继承的白盒复用特点,一定程度上破坏了类的封装特性,因为这会将父类的实现细节暴露给子类
(5)组合属于黑盒复用。被包含对象的内部细节对外是不可见的,所以它的封装性相对较好,实现上相互依赖比较小
(6)组合中被包含类会随着包含类创建而创建,消亡而消亡。组合属于黑盒复用,并且可以通过获取其它具有相同类型的对象引用或指针,在运行期间动态的定义组合。而缺点就是致使系统中的对象过多。
(7)OO设计原则是优先组合,而后继承
多继承及其二义性问题
多继承
(1)多继承就是一个子类有多个父类
(2)多继承演示
(3)多继承和单继承的原理,效果并无明显区别
(4)多继承会导致二义性问题
多继承的二义性问题1
(1)场景:C多继承自A和B,则C中调用A和B的同名成员时会有二义性
(2)原因:C从A和B各自继承了一个同名(不同namespace域)成员,所以用C的对象来调用时编译器无法确定我们想调用的是哪一个
(3)解决办法1:避免出现,让A和B的public成员命名不要重复冲突。但这个有时不可控。
(4)解决办法2:编码时明确指定要调用哪一个,用c.A::func()明确指定调用的是class A的func而不是class B的
(5)解决办法3:在C中重定义func,则调用时会调用C中的func,A和B中的都被隐藏了
(6)总结:能解决,但是都没有很好的解决。
多继承的二义性问题2
(1)场景:菱形继承问题。即A为祖类,B1:A, B2:A, C:B1,B2,此时用C的对象调用A中的某个方法时会有二义性
(2)分析:c.func()有二义性,c.A::func()也有二义性,但是c.B1::func()和c.B2::func()却没有二义性
(3)解决办法:和问题1中的一样,但是问题2更隐蔽,也更难以避免
// 祖类
class Aa {
public:
void show() { std::cout << "A's method" << std::endl; }
};
// 派生类 B1 和 B2,从 A 继承
class B1 : public Aa {
public:
void show() { std::cout << "B1's show" << std::endl; }
void showB1() { std::cout << "B1's method" << std::endl; }
};
class B2 : public Aa {
public:
void show() { std::cout << "B2's show" << std::endl; }
void showB2() { std::cout << "B2's method" << std::endl; }
};
// 派生类 C,从 B1 和 B2 继承
class C : public B1, public B2 {
public:
void showC() { std::cout << "C's method" << std::endl; }
};
int test070103() {
C c;
// c.Aa::show(); // 调用 A 的方法 不可以
c.Aa::B1::show(); // 调用 A 的方法 不可以
c.B2::Aa::show(); // 调用 A 的方法 不可以
c.B1::show(); // 调用 B1 的方法
c.B2::show(); // 调用 B2 的方法
c.showB1(); // 调用 B1 的方法
c.showB2(); // 调用 B2 的方法
c.showC(); // 调用 C 的方法
return 0;
}
虚继承解决菱形继承的二义性问题
虚继承怎么用
(1)场景:菱形继承导致二义性问题,本质上是在孙子类C中有B1和B2中包含的2份A对象,所以有了二义性。
(2)虚继承解决方案:让B1和B2虚继承A,C再正常多继承B1和B2即可
(3)虚继承就这么简单,就是为了解决菱形继承的二义性问题而生,和虚函数(为了实现多态特性)并没有直接关系
虚继承的实现原理
(1)虚继承的原理是:虚基类表指针vbptr和虚基类表virtual table
(2)参考:https://blog.csdn.net/xiejingfa/article/details/48028491
总结
理解什么是组合,一个class类里面有很多其他class类类型的成员变量
理解组合和继承的区别
二义性:执行了一个函数,确不一定是自己想指定的那个
解决方案:用c.A::func()明确的指定调用的是class
虚继承有点像条件编译,引入一次就好,不用重复引入,也能达到引入效果
学习记录,侵权联系删除。
来源:朱老师物联网大课堂