博客
关于我
C++基础(十八):继承(重点)
阅读量:799 次
发布时间:2023-04-17

本文共 4307 字,大约阅读时间需要 14 分钟。

C++继承机制详解

一、继承的概念及定义

1.1 继承的概念

继承是面向对象编程中实现代码复用的重要机制。它允许在保持基类功能的基础上,通过派生类扩展功能。继承体现了从简单到复杂的层次结构,帮助开发者更好地反映现实世界的事物层次。

示例:

class Person {
public:
void Print() {
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
protected:
string _name = "peter";
int _age = 18;
};
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;
}

继承带来的作用:

  • 派生类可以拥有基类的成员变量和成员函数。
  • 派生类可以定义自己的成员变量和函数。

1.2 继承定义

1.2.1 定义格式

基类和派生类的关系定义如下:

class Person { /*基类定义*/ };
class Student : public Person { /*派生类定义*/ };
  • public:默认的继承方式,即派生类可以访问基类的所有成员。

1.2.2 继承关系与访问限定符

在C++中,继承时可以指定访问级别:

class Derived : private Base { }; // 派生类不能直接访问基类的private成员
class Derived : protected Base { }; // 派生类可以访问基类的protected成员
class Derived : public Base { }; // 默认继承方式,派生类可以访问基类的所有成员

访问级别的含义:

  • private:派生类无法访问基类的private成员。
  • protected:派生类可以访问基类的protected成员,但无法访问基类的private成员。
  • public:派生类可以访问基类的所有成员。

1.2.3 继承后的成员访问方式

基类成员在派生类中的访问方式取决于继承方式:

  • public:派生类可以直接访问基类的成员。
  • protected:派生类可以通过基类的protected成员访问。
  • private:派生类无法访问基类的private成员。

二、基类与派生类对象的赋值规则

2.1 子类可以赋值给父类

  • 派生类对象可以赋值给基类对象、基类指针或基类引用。
  • 这种赋值操作叫做“切割”或“截断”。

示例:

class Person {
protected:
string _name;
string _sex;
int _age;
};
class Student : public Person {
public:
int _No; // 学号
};
void Test() {
Person p;
Student s;
// 派生类对象赋值给基类对象
Person p = s;
Person* ptr = &s;
Person& rp = s;
// 基类指针或引用可以安全地指向派生类对象
ptr = &s;
Student* ps = static_cast
(ptr);
ps->_No = 10;
ptr = &p;
Student* ps2 = static_cast
(ptr); // 可能会导致越界访问
ps2->_No = 10;
}

2.2 父类不能赋值给子类

  • 基类对象不能直接赋值给派生类对象。

示例:

// 这段代码会报错
Student s = p; // 错误,父类对象不能赋值给子类对象

2.3 父类赋值给子类的特殊情况

  • 基类的指针或引用可以通过强制类型转换赋值给派生类的指针或引用,但前提是基类指针指向派生类对象。
  • 使用 dynamic_cast 来安全地进行类型转换。

示例:

class Teacher : public Person {
public:
int _jobid; // 工号
};
void Assign() {
Person* basePtr = new Person();
Teacher* teacherPtr = new Teacher();
// 安全地将基类指针赋值给派生类指针
teacherPtr = dynamic_cast
(basePtr);
teacherPtr->_jobid = 123;
// 不安全的转换示例
basePtr = new Person();
teacherPtr = static_cast
(basePtr); // 可能导致越界访问
}

三、继承中的作用域

3.1 隐藏的概念

在继承体系中,基类和派生类具有独立的作用域。

  • 如果派生类和基类中存在同名成员(变量或函数),派生类的成员会屏蔽基类的成员。
  • 这种现象称为“隐藏”,尤其是在同名成员函数时。

3.2 如何解决隐藏问题

  • 在派生类的成员函数中,使用 Base::member 来直接访问基类的成员。
  • 避免在派生类和基类中定义相同的成员变量或函数。

示例:

class A {
public:
void fun() {
cout << "func()" << endl;
}
};
class B : public A {
public:
void fun(int i) {
A::fun(); // 访问基类的成员
cout << "func(int i) - " << i << endl;
}
};

3.3 注意事项

  • 避免在继承体系中定义同名的成员变量或函数。
  • 在派生类中使用 using Base::Base; 来显式地声明基类成员。

四、继承的实际应用

4.1 实例化继承

通过继承,可以将基类的功能实例化为派生类,同时扩展功能。

示例:

class Animal {
public:
void Eat() {
cout << "动物在吃..." << endl;
}
};
class Dog : public Animal {
public:
void Bark() {
cout << "狗在叫..." << endl;
}
};
int main() {
Animal* animal = new Animal();
Dog* dog = new Dog();
animal->Eat();
dog->Eat(); // 调用派生类的Eat函数
dog->Bark();
delete animal;
delete dog;
}

4.2 继承与多态

在支持多态的语言中,继承可以实现运行时绑定。

  • 使用 dynamic_cast 进行安全的类型转换。
  • 基类方法可以有不同的实现方式。

示例:

class Shape {
public:
virtual void Draw() = 0;
};
class Circle : public Shape {
public:
void Draw() {
cout << "画一个圆..." << endl;
}
};
class Triangle : public Shape {
public:
void Draw() {
cout << "画一个三角形..." << endl;
}
};
void DrawShape(Shape* shape) {
if (dynamic_cast
(shape)) {
shape->Draw();
cout << "是圆" << endl;
} else if (dynamic_cast
(shape)) {
shape->Draw();
cout << "是三角形" << endl;
}
}

五、继承的常见问题

5.1 如何避免成员的隐藏

  • 避免在派生类和基类中重复定义同名的成员。
  • 使用 using 语句显式地声明基类成员。

示例:

class Person {
public:
void Print() {
cout << "name:" << _name << endl;
cout << "age:" << _age << endl;
}
};
class Student : public Person {
public:
using Person::Print; // 显式声明使用基类的Print函数
void NewPrint() {
cout << "学号:" << _No << endl;
Print(); // 调用基类的Print函数
}
};

5.2 如何处理多个继承层次

  • 使用 struct 关键字时,默认使用 public 继承。
  • 在实际开发中,建议显式声明继承方式。

通过以上内容,我们可以清晰地理解C++继承的核心概念及其应用场景。掌握了继承的规则和注意事项后,可以在实际开发中灵活运用这一强大的工具。

转载地址:http://zagfk.baihongyu.com/

你可能感兴趣的文章
mysql8.0新特性-自增变量的持久化
查看>>
Mysql8.0注意url变更写法
查看>>
Mysql8.0的特性
查看>>
MySQL8修改密码报错ERROR 1819 (HY000): Your password does not satisfy the current policy requirements
查看>>
MySQL8修改密码的方法
查看>>
Mysql8在Centos上安装后忘记root密码如何重新设置
查看>>
Mysql8在Windows上离线安装时忘记root密码
查看>>
MySQL8找不到my.ini配置文件以及报sql_mode=only_full_group_by解决方案
查看>>
mysql8的安装与卸载
查看>>
MySQL8,体验不一样的安装方式!
查看>>
MySQL: Host '127.0.0.1' is not allowed to connect to this MySQL server
查看>>
Mysql: 对换(替换)两条记录的同一个字段值
查看>>
mysql:Can‘t connect to local MySQL server through socket ‘/var/run/mysqld/mysqld.sock‘解决方法
查看>>
MYSQL:基础——3N范式的表结构设计
查看>>
MYSQL:基础——触发器
查看>>
Mysql:连接报错“closing inbound before receiving peer‘s close_notify”
查看>>
mysqlbinlog报错unknown variable ‘default-character-set=utf8mb4‘
查看>>
mysqldump 参数--lock-tables浅析
查看>>
mysqldump 导出中文乱码
查看>>
mysqldump 导出数据库中每张表的前n条
查看>>