class Derived(派生类): access-specifier(继承方法) Base(基类)
👽示例代码
c++复制代码
// 基类
class Shape {
public:
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
protected:
int width;
int height;
};
// 派生类
class Rectangle: public Shape {
public:
int getArea() { return (width * height); }
};
...
Rectangle Rect;
Rect.setWidth(5);
Rect.setHeight(7);
int area = Rect.getArea();
👾多继承
一个类继承自多个基类1:n,使用类派生列表来指定基类
👽语法格式
c++复制代码
class Derived : access-specifier Base1, access-specifier Base2 , ...
👽示例代码
c++复制代码
// 基类 Shape
class Shape {
public:
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
protected:
int width;
int height;
};
// 基类 PaintCost
class PaintCost {
public:
int getCost(int area) { return area * 70; }
};
// 派生类
class Rectangle: public Shape, public PaintCost {
public:
int getArea() { return (width * height); }
};
...
Rectangle Rect;
// 使用继承自 Shape 类的方法 setWidth()、setHeight()
Rect.setWidth(5);
Rect.setHeight(7);
// 使用继承自 PaintCost 类的方法 getCost(area)
cout << "Cost = " << Rect.getCost(Rect.getArea()) << endl;
👾菱形继承
派生类通过间接或直接的方式多次继承同一个祖先基类 ,不仅占用较多的存储空间,还容易产生命名冲突。
类 A 派生 B 和 C ,类 D 继承自 B 和 C
数据冗余 :类 A 中的成员继承到类 D 中变成两份,来自 A-->B-->D 和 A-->C-->D ,存储两份 m_a,造成数据冗余
命名冲突 :在类 D 中直接访问 a 会产生歧义,编译器不知道它究竟来自 A-->B-->D ,还是 A-->C-->D
c++复制代码
// 间接基类A
class A {
protected:
int m_a;
};
// 直接基类B
class B: public A {
protected:
int m_b;
};
// 直接基类C
class C: public A {
protected:
int m_c;
};
// 派生类D
class D: public B, public C {
public:
void seta(int a) { m_a = a; } // 命名冲突
void setb(int b) { m_b = b; } // 正确
void setc(int c) { m_c = c; } // 正确
void setd(int d) { m_d = d; } // 正确
private:
int m_d;
};
...
D d;
class A : public B {}; // A继承自B
class B : public A {}; // B继承自A
👻 虚继承
👾概念
让某个类做出声明,承诺愿意共享它的基类,被共享的基类就称为虚基类。
不论虚基类在继承体系中出现了多少次,在派生类中都只包含一份虚基类的成员
👾语法格式
在继承方式前面加上 关键字 virtual 实现虚继承
c++复制代码
class Derived(派生类): virtual(虚继承) access-specifier(继承方法) Base(基类)
👾示例代码
c++复制代码
// 间接基类A
class A {
protected:
int m_a;
};
// 直接基类B
class B: virtual public A { //虚继承
protected:
int m_b;
};
// 直接基类C
class C: virtual public A { //虚继承
protected:
int m_c;
};
// 派生类D
class D: public B, public C {
public:
void seta(int a) { m_a = a; } // 正确
void setb(int b) { m_b = b; } // 正确
void setc(int c) { m_c = c; } // 正确
void setd(int d) { m_d = d; } // 正确
private:
int m_d;
};
...
D d;
会发现虚继承的一个不太直观的特征:必须在虚派生的真实需求出现前就已经完成虚派生的操作 。在上图中,当定义 D 类时才出现了对虚派生的需求 ,如果 B 类和 C 类不是从 A 类虚派生得到的,那么 D 类还是会保留 A 类的两份成员。换个角度讲,虚派生只影响从指定虚基类的派生类中进一步派生出来的类,它不会影响派生类本身
在实际开发中,位于中间层次的基类将其继承声明为虚继承一般不会带来什么问题。 虚基类成员二义性
假设 A 定义了一个名为 x 的成员变量,当在 D 中直接访问 x 时,会有三种可能性:
如果 B 和 C 中都没有 x 的定义, x 将被解析为 A 的成员,不存在二义性
如果 B 或 C 其中的一个类定义了 x,派生类的 x 比虚基类的 x 优先级更高,不存在二义性
如果 B 和 C 中都定义了 x,直接访问 x 将产生二义性问题
不提倡在程序中使用多继承
👻 访问控制
👾成员变量
派生类可以访问基类中所有的非私有成员 。基类如不想被派生类访问,应声明为 private
访问
public
protected
private
同一个类
√
√
√
派生类(继承)
√
√
×
外部类
√
×
×
可以通过 调用基类提供的公有成员函数入口 来访问基类的私有成员变量
c++复制代码
class Base {
private:
int privateVar;
public:
Base() : privateVar(10) {}
void accessPrivateVar() {
cout << "Base privateVar: " << privateVar << endl;
}
};
class Derived : public Base {
public:
void accessBasePrivateVar() {
accessPrivateVar(); // 通过调用基类的公有成员函数来访问基类的私有成员变量
}
};
...
Derived d;
d.accessBasePrivateVar(); // 输出 Base privateVar: 10