一、继承与静态成员
基类定义了static 静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子
类,都只有一个 static 成员实例 。
class person
{
public:
person(const char* name = "lisi")
:_name(name)
{}
public:
string _name;
// 静态成员属性
static int count;
};
class worker : public person
{
public:
protected:
string _job_number = "111";
};
// 静态成员属性需要在类外定义
int person::count = 0;
void Test2(void)
{
person pobj;
worker wobj;
pobj._name = "wangwu";
cout << pobj._name << endl;
cout << wobj._name << endl;
cout << "wobj.count: " << wobj.count << endl;
// 基类更改这个静态成员
pobj.count = 5;
cout << "wobj.count: " << wobj.count << endl;
cout << "&pobj.count = " << &pobj.count << endl;
cout << "&wobj.count = " << &wobj.count << endl;
}
静态成员属性需要在类外定义;
上面的_name,基类对象和派生类对象各自私有一份,而对于静态成员变量 count,派生类继承的 count 和基类里面的 count 是同一份。
即基类里面的静态成员,无论有多少个派生类,它们都共享同一个静态成员。
二、单继承
单继承:一个派生类只有一个直接基类,我们称这个继承关系为单继承。
class Person{};
class Worker : public Person{};
class Teacher : public Worker{};
三、多继承
多继承:一个派生类有两个或以上直接基类时称这个继承关系为多继承。
四、菱形继承
类似以上继承为菱形继承。
菱形继承会产生数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份。
二义性无法明确知道访问的是哪一个,需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决。
那么该怎么解决呢?
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。
五、虚拟继承
为解决菱形继承的二义性和数据冗余,我们采用菱形继承,我们看以下代码:
class A
{
public:
int _a;
};
// class B : public A
class B : virtual public A
{
public:
int _b;
};
// class C : public A
class C : virtual public A
{
public:
int _c;
};
class D : public B, public C
{
public:
int _d;
};
int main()
{
D d;
d.B::_a = 1;
d.C::_a = 2;
d._b = 3;
d._c = 4;
d._d = 5;
return 0;
}
六、总结
1.很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。
2.多继承可以认为是C++的缺陷之一,很多后来的语言都没有多继承,如Java
3.继承和组合的区别?什么时候用继承?什么时候用组合?
继承和组合是面向对象编程中两种常见的代码重用机制,它们有不同的使用场景和特点。
继承:
- 继承是一种“is-a”关系,它描述了两个类之间的一种层次结构,子类继承了父类的属性和方法,并且可以添加自己的新属性和方法。
- 适合于在现有类的基础上进行扩展,通过重用现有类的代码来实现新类的功能。
- 通常用于描述一种分类或分类的关系,子类是父类的特例,具有更具体的特征或行为。
- 适用于需要实现代码的重用和扩展的情况。
组合:
- 组合是一种“has-a”关系,它描述了两个类之间的一种包含关系,一个类包含另一个类的实例作为其成员变量。
- 适合于描述一种包含关系,其中一个类包含另一个类的实例,并且通过这种组合来实现更复杂的功能。
- 通常用于描述一种组合或拥有关系,其中一个类包含了另一个类的实例作为其一部分。
- 适用于需要将不同的类组合在一起实现某个功能的情况,而不是通过继承来实现。
在未来代码设计中,遵循的设计原则是:低耦合,高内聚。
在选择继承还是组合时,可以考虑以下几点:
- 代码重用性:如果需要重用现有类的代码并扩展其功能,则可以选择继承。如果只是需要利用现有类的功能而不需要扩展其功能,则可以选择组合。
- 耦合性:继承会增加类之间的耦合性,子类与父类之间存在较强的依赖关系,而组合可以降低耦合性,类之间的关系更灵活。
- 设计灵活性:组合比继承更灵活,因为可以随时更改组合关系,而不会影响类的结构。继承则更加静态,子类的结构受限于父类的定义。
4.如何定义一个无法被继承的类?
第一种方式C++98,将基类的构造私有化,派生类继承这个基类,在实例化对象时,需要调用基类的构造,但由于基类的构造已经私有化,故会编译报错。
class A
{
public:
//将基类的构造函数私有化
private:
A(int a = int())
:_a(a)
{
cout << "A()" << endl;
};
protected:
int _a;
};
class B : public A
{
protected:
int _b;
};
对于 C++11 的做法是:通过关键字 final,被final修饰的类无法被继承,编译器会强制检查。
// 用 final 修饰 A类, 此时A类无法被继承
class A final
{
public:
A(int a = int())
:_a(a)
{
cout << "A()" << endl;
}
protected:
int _a;
};
class B : public A
{
protected:
int _b;
};