赞
踩
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
- class Person
- {
- public:
- void Print()
- {
- cout << "name:" << _name << endl;
- cout << "age:" << _age << endl;
- }
- protected:
- string _name = "peter"; // 姓名
- int _age = 18; // 年龄
- };
-
- // 继承后父类的Person的成员(成员函数+成员变量)都会变成子类的一部分。这里体现出了Student和Teacher复用了Person的成员。
-
- class Student : public Person
- {
- protected:
- int _stuid; // 学号
- };
-
- class Teacher : public Person
- {
- protected:
- int _jobid; // 工号
- };
-
- int main()
- {
- Student s;
- Teacher t;
- s.Print();
- t.Print();
- return 0;
- }
-
- //name:peter
- //age:18
- //name:peter
- //age:18
Person是父类,也称作基类。Teacher是子类,也称作派生类。
类成员/继承方式 | public继承 | protected继承 | private继承 |
基类的public成员 | 派生类的public成员 | 派生类的protected 成员 | 派生类的private 成员 |
基类的protected 成员 | 派生类的protected成员 | 派生类的protected 成员 | 派生类的private 成员 |
基类的private成 员 | 在派生类中不可见 | 在派生类中不可见 | 在派生类中不可见 |
基类 private
成员的访问:
private
成员在派生类中确实是不可见的。无论继承方式如何,这些成员对派生类都是不可访问的。protected
或 public
方法间接访问,或者在基类中提供友元类或友元函数。基类 protected
成员的访问:
protected
成员在派生类中是可访问的,允许派生类访问和修改这些成员,但它们在派生类外部仍然不可见。protected
成员的访问权限是为了支持派生类访问,限制了直接外部访问。继承方式与访问控制:
private
成员在子类中是不可见的,protected
成员在子类中可见。实际访问方式取决于基类成员的访问修饰符和继承方式的组合。public
、protected
、private
)会影响基类成员在派生类中的访问级别。通常,public
继承使基类的 public
成员保持 public
,protected
继承使基类的 public
和 protected
成员都变为 protected
,private
继承则使基类的所有 public
和 protected
成员都变为 private
。默认继承方式:
class
默认使用 private
继承。struct
默认使用 public
继承。继承的使用建议:
public
继承是最常用的,因为它能够支持广泛的代码复用和接口设计。protected
和 private
继承通常用于更特殊的场景,比如在实现某些设计模式时。protected
继承可以防止外部代码直接使用基类的接口,而 private
继承则主要用于实现内部细节的封装。- class Person
- {
- public :
- void Print ()
- {
- cout<<_name <<endl;
- }
-
- protected :
- string _name ; // 姓名
-
- private :
- int _age ; // 年龄
- };
public
继承- class Student : public Person
- {
- protected:
- int _stunum; // 学号
- };
-
-
- Student s;
- s.Print(); // 可以访问
- // s._name; // 不可访问,编译错误
- // s._age; // 不可访问,编译错误
protected
继承- class Student : protected Person
- {
- protected:
- int _stunum; // 学号
- };
-
-
- Student s;
- // s.Print(); // 不可访问,编译错误
- // s._name; // 不可访问,编译错误
- // s._age; // 不可访问,编译错误
private
继承- class Student : private Person
- {
- protected:
- int _stunum; // 学号
- };
-
-
- Student s;
- // s.Print(); // 不可访问,编译错误
- // s._name; // 不可访问,编译错误
- // s._age; // 不可访问,编译错误
-
- class Base {
- public:
- int baseVar;
- };
-
- class Derived : public Base {
- public:
- int derivedVar;
- };
-
- Derived d;
- d.baseVar = 1;
- d.derivedVar = 2;
-
- Base b = d; // 切片,Base 对象只保留 baseVar,丢失 derivedVar
基类对象不包含派生类的所有信息,因此无法赋值给派生类对象。这会导致派生类独有的成员无法初始化或被错误地初始化。
向上转换(Upcasting):派生类的指针或引用可以赋值给基类的指针或引用。这是安全的,因为派生类总是包含基类的部分。
- Derived* d = new Derived();
- Base* b = d; // 向上转换
向下转换(Downcasting):基类的指针或引用可以通过强制类型转换赋值给派生类的指针或引用,但前提是这个基类指针或引用实际指向的是派生类的对象,否则会导致未定义行为。
dynamic_cast
进行向下转换,并通过 RTTI 确保转换的安全性:- Base* b = new Derived(); //这个基类指针或引用实际指向的是派生类的对象
- Derived* d = dynamic_cast<Derived*>(b); // 安全的向下转换
-
- if (d != nullptr) {
- // 转换成功,可以安全使用 d
- }
dynamic_cast
来在运行时检查和转换类型。dynamic_cast
只能用于多态类型(即至少有一个虚函数的类),它会在类型不匹配时返回 nullptr
。基类名::成员名
的形式来访问。 - class Base {
- public:
- int value = 10;
- void display() { cout << "Base display" << endl; }
- };
-
- class Derived : public Base {
- public:
- int value = 20; // 隐藏了 Base::value
- void display() { cout << "Derived display" << endl; } // 隐藏了 Base::display()
- };
-
- Derived d;
- cout << d.value << endl; // 输出 20,访问的是 Derived::value
- cout << d.Base::value << endl; // 输出 10,访问的是 Base::value
-
- d.display(); // 输出 "Derived display"
- d.Base::display(); // 输出 "Base display"
- class Base {
- public:
- Base(int x) : value(x) {}
- protected:
- int value;
- };
-
- class Derived : public Base {
- public:
- Derived(int x, int y) : Base(x), derivedValue(y) {}
- protected:
- int derivedValue;
- };
Derived(const Derived& other) : Base(other), derivedValue(other.derivedValue) {}
operator=
- Derived& operator=(const Derived& other) {
- if (this != &other) {
- Base::operator=(other); // 调用基类的赋值运算符
- derivedValue = other.derivedValue;
- }
- return *this;
- }
- ~Derived() {
- // 派生类的清理工作
- // 自动调用基类的析构函数
- }
virtual
,派生类的析构函数会隐藏基类的析构函数。这意味着通过基类指针删除派生类对象时,只有基类部分会被释放,可能导致资源泄露。因此,如果类可能作为基类使用,析构函数通常应声明为 virtual
。- class Base {
- public:
- virtual ~Base() {} // 推荐用法
- };
-
- class Derived : public Base {
- public:
- ~Derived() {} // 派生类析构函数
- };
- #include <iostream>
- using namespace std;
-
- class Student; // 前向声明
-
- class Person {
- public:
- Person(const string& name = "") : _name(name) {}
- friend void Display(const Person& p, const Student& s); // 友元函数声明
- protected:
- string _name; // 姓名
- };
-
- class Student : public Person {
- public:
- Student(const string& name = "", int stuNum = 0) : Person(name), _stuNum(stuNum) {}
- protected:
- int _stuNum; // 学号
- };
-
- void Display(const Person& p, const Student& s) {
- cout << "Name: " << p._name << endl;
- cout << "Student Number: " << s._stuNum << endl;
- }
-
- int main() {
- Person p("Alice");
- Student s("Bob", 12345);
- Display(p, s);
- return 0;
- }
基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子
类,都只有一个static成员实例 。
- #include <iostream>
- using namespace std;
-
- class Person {
- public:
- Person() { ++_count; } // 构造函数中增加计数
- static int _count; // 静态数据成员声明
- protected:
- string _name; // 姓名
- };
-
- int Person::_count = 0; // 静态数据成员定义
-
- class Student : public Person {
- protected:
- int _stuNum; // 学号
- };
-
- class Graduate : public Student {
- protected:
- string _seminarCourse; // 研究科目
- };
-
- void TestPerson() {
- Student s1;
- Student s2;
- Student s3;
- Graduate s4;
-
- // 输出人数
- cout << "人数: " << Person::_count << endl; //4
-
- // 注意: 直接访问和修改 Person::_count 是有效的,因为静态数据成员是共享的
- // Student::_count = 0; // 这行代码实际上不影响 Person::_count,因为静态数据成员是共享的
-
- // 重置静态数据成员 (这行代码将 Person::_count 设置为0)
- Person::_count = 0;
-
- // 输出人数
- cout << "人数: " << Person::_count << endl; //0
- }
-
- int main() {
- TestPerson();
- return 0;
- }
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承:菱形继承是多继承的一种特殊情况。
菱形继承的问题:从下面的对象成员模型构造,可以看出菱形继承有数据冗余和二义性的问题。
在Father的对象中Person成员会有两份。
- class Person
- {
- public :
- string _name ; // 姓名
- };
-
- class Student : public Person
- {
- protected :
- int _num ; //学号
- };
- class Teacher : public Person
- {
- protected :
- int _id ; // 职工编号
- };
-
- class Father : public Student, public Teacher
- {
- protected :
- string _childname ; // 孩子名字
- };
- void Test ()
- {
- // 这样会有二义性无法明确知道访问的是哪一个
- Father a ;
- a._name = "peter";
- // 需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
- a.Student::_name = "xxx";
- a.Teacher::_name = "yyy";
- }
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
- class Person
- {
- public :
- string _name ; // 姓名
- };
-
- class Student : virtual public Person
- {
- protected :
- int _num ; //学号
- };
-
- class Teacher : virtual public Person
- {
- protected :
- int _id ; // 职工编号
- };
-
- class father : public Student, public Teacher
- {
- protected :
- string _childname ;
- };
- void Test ()
- {
- Assistant a ;
- a._name = "peter";
- }
在内存中,A的内存地址会在D后面,这里B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
继承(is-a)每个派生类对象都是一个基类对象。:
Cat
是 Animal
的一种。- #include <iostream>
- #include <string>
- using namespace std;
-
- class Shape {
- public:
- Shape(const string& color) : _color(color) {}
- virtual void draw() const = 0; // 纯虚函数,强制子类实现
- protected:
- string _color; // 颜色属性
- };
-
- class Circle : public Shape {
- public:
- Circle(const string& color, double radius)
- : Shape(color), _radius(radius) {}
- void draw() const override {
- cout << "Drawing a " << _color << " circle with radius " << _radius << endl;
- }
- private:
- double _radius;
- };
-
- class Rectangle : public Shape {
- public:
- Rectangle(const string& color, double width, double height)
- : Shape(color), _width(width), _height(height) {}
- void draw() const override {
- cout << "Drawing a " << _color << " rectangle with width " << _width << " and height " << _height << endl;
- }
- private:
- double _width, _height;
- };
-
- int main() {
- Circle c("red", 5.0);
- Rectangle r("blue", 4.0, 6.0);
-
- c.draw();
- r.draw();
-
- return 0;
- }
组合(has-a)假设B组合了A,每个B对象中都有一个A对象:
- #include <iostream>
- #include <string>
- using namespace std;
-
- class Color {
- public:
- Color(const string& color) : _color(color) {}
- string getColor() const { return _color; }
- private:
- string _color;
- };
-
- class Shape {
- public:
- Shape(const Color& color) : _color(color) {}
- virtual void draw() const = 0; // 纯虚函数,强制子类实现
- protected:
- Color _color; // 通过组合来管理颜色
- };
-
- class Circle : public Shape {
- public:
- Circle(const Color& color, double radius)
- : Shape(color), _radius(radius) {}
- void draw() const override {
- cout << "Drawing a " << _color.getColor() << " circle with radius " << _radius << endl;
- }
- private:
- double _radius;
- };
-
- class Rectangle : public Shape {
- public:
- Rectangle(const Color& color, double width, double height)
- : Shape(color), _width(width), _height(height) {}
- void draw() const override {
- cout << "Drawing a " << _color.getColor() << " rectangle with width " << _width << " and height " << _height << endl;
- }
- private:
- double _width, _height;
- };
-
- int main() {
- Color red("red");
- Color blue("blue");
-
- Circle c(red, 5.0);
- Rectangle r(blue, 4.0, 6.0);
-
- c.draw();
- r.draw();
-
- return 0;
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。