1.继承的概念和定义
1.1概念
C++中的继承是面向对象编程(OOP)中的一个重要概念,它允许你创建一个类(称为子类或派生类),这个类可以继承另一个类(称为基类或父类)的属性和方法。
继承的基本语法:
class BaseClass {
public:
int baseVariable;
void baseFunction() {
// 基类的函数
}
};
class DerivedClass : public BaseClass { // public继承
public:
int derivedVariable;
void derivedFunction() {
// 派生类的函数
}
};
继承的类型:
公有继承:父类的公共成员和保护成员会被子类继承,并且保持其访问权限。
class Base {
public:
int a;
protected:
int b;
private:
int c;
};
class Derived : public Base {
public:
void show() {
// 子类可以访问a和b
cout << a << " " << b << endl;
}
};
保护继承:父类的公共成员和保护成员会被子类继承,但它们的访问权限会变为受保护。
class Derived : protected Base {
public:
void show() {
// 子类可以访问a和b,但外部无法访问
cout << a << " " << b << endl;
}
};
私有继承:父类的公共成员和保护成员会被子类继承,但它们的访问权限会变为私有。
class Derived : private Base {
public:
void show() {
// 子类可以访问a和b,但外部无法访问
cout << a << " " << b << endl;
}
};
1.2 继承基类成员访问方式的变化
1.2.1 公有继承
公有继承是最常见的继承类型,它表示“是一个”的关系,子类是基类的扩展。在公有继承中:
- 基类的 public 成员在子类中仍然是 public。
- 基类的 protected 成员在子类中仍然是 protected。
- 基类的 private 成员不能被子类直接访问,但可以通过基类的公共或保护方法间接访问。
class Base {
public:
int publicVar;
protected:
int protectedVar;
private:
int privateVar;
};
class Derived : public Base {
public:
void access() {
// 可以访问基类的public和protected成员
publicVar = 10;
protectedVar = 20;
// privateVar 不能直接访问
}
};
1.2.2 保护继承
保护继承表示“实现”关系,子类继承了基类的成员,但在子类之外,这些成员变成了保护成员。它限制了外部访问这些成员,但子类内部仍然可以访问。
- 基类的 public 成员在子类中变为 protected。
- 基类的 protected 成员在子类中保持 protected。
- 基类的 private 成员依然无法访问。
class Derived : protected Base {
public:
void access() {
// publicVar 和 protectedVar 变成了protected成员
publicVar = 10; // 在Derived中可以访问
protectedVar = 20; // 在Derived中可以访问
// privateVar 依然不能访问
}
};
int main() {
Derived d;
// d.publicVar = 30; // 外部无法访问publicVar
}

1.2.3 私有继承
私有继承表示“实现”关系,子类继承了基类的功能,但外部代码无法访问基类的任何成员。通常,子类通过私有继承来实现其功能,但不想暴露基类的接口。
- 基类的 public 成员在子类中变为 private。
- 基类的 protected 成员在子类中变为 private。
- 基类的 private 成员依然无法访问。
class Derived : private Base {
public:
void access() {
// 基类的public和protected成员变为private
publicVar = 10;
protectedVar = 20;
// privateVar 依然不能访问
}
};
int main() {
Derived d;
// d.publicVar = 30; // 外部无法访问publicVar
}

基类和派⽣类间的转换
2.基类和派生类间的转换
public 继承的派生类对象可以赋值给基类的指针或引用,这是因为派生类是基类的一种特殊类型。这里的“切片”或“切割”是一个形象的说法,指的是当派生类对象赋值给基类指针或引用时,派生类对象中基类部分的成员会被提取出来,基类指针或引用指向的是派生类对象中“切出来的”基类部分。
2.1.基类对象不能赋值给派生类对象
这是因为派生类继承了基类,但派生类的对象包含了基类的成员和派生类自己的成员。基类对象并没有派生类对象的那些额外成员,所以不能直接赋值给派生类对象。换句话说,派生类的成员是基类所没有的,因此不能用一个基类对象来赋值给派生类对象。
class Base {
public:
int baseVar;
};
class Derived : public Base {
public:
int derivedVar;
};
int main() {
Base b; // 基类对象
Derived d; // 派生类对象
// 错误:基类对象不能赋值给派生类对象
// d = b; // 编译错误
return 0;
}
2.2 基类对象赋值给基类指针或引用——切片问题
当我们把派生类对象赋值给基类指针或引用时,派生类的对象会被“切割”,也就是只有派生类中继承自基类的部分会被保留下来,指针或引用指向的只有基类部分,派生类的额外部分(如成员变量)会被丢弃。
class Base {
public:
int baseVar;
virtual void show() { std::cout << "Base class" << std::endl; }
};
class Derived : public Base {
public:
int derivedVar;
void show() override { std::cout << "Derived class" << std::endl; }
};
int main() {
Derived d; // 派生类对象
d.baseVar = 10; // 派生类部分
d.derivedVar = 20; // 派生类特有成员
Base* b = &d; // 基类指针指向派生类对象
b->show(); // 调用派生类的show()
std::cout << "baseVar: " << b->baseVar << std::endl; // 访问基类成员
// 注意:基类指针只能访问基类成员,derivedVar 不可访问
// std::cout << "derivedVar: " << b->derivedVar << std::endl; // 编译错误
return 0;
}
在这个例子中,d 是一个派生类对象,它被赋值给了基类指针 b。尽管 d 包含了基类和派生类的成员,但 b 只能够访问到 Base 类的成员,Derived 类的成员会被“切割”掉,不可访问。
3. 继承中的作用域
隐藏规则:
1. 在继承体系中基类和派⽣类都有独立的作用域。
2. 派生类和基类中有同名成员,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。 (在派生类成员函数中,可以使用基类::基类成员显示访问)
3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
#include <iostream>
using namespace std;
class Base {
public:
void show() {
cout << "Base class show function" << endl;
}
};
class Derived : public Base {
public:
void show() { // 这里会隐藏基类中的show()函数
cout << "Derived class show function" << endl;
}
void showBase() {
Base::show(); // 使用基类作用域来访问被隐藏的show函数
}
};
int main() {
Derived obj;
obj.show(); // 调用派生类的show函数
obj.showBase(); // 调用基类的show函数
return 0;
}
输出:
Derived class show function
Base class show function
Derived 类中的 show() 函数隐藏了基类 Base 中的 show() 函数。要想在派生类中访问基类的 show() 函数,可以通过 Base::show() 来实现。
4. 派生类的默认成员函数
1.在 C++ 中,当派生类没有默认构造函数时,必须在派生类的构造函数中显式调用基类的构造函数。这通常通过初始化列表来实现。
#include <iostream>
using namespace std;
class Base {
public:
Base(int x) { // 基类的构造函数需要一个参数
cout << "Base class constructor called with value " << x << endl;
}
};
class Derived : public Base {
public:
Derived(int x, int y) : Base(x) { // 在初始化列表中调用基类的构造函数
cout << "Derived class constructor called with value " << y << endl;
}
};
int main() {
Derived obj(10, 20); // 创建派生类对象,传递给基类和派生类构造函数
return 0;
}
输出:
Base class constructor called with value 10
Derived class constructor called with value 20
2.在 C++ 中,派生类的拷贝构造函数也必须显式调用基类的拷贝构造函数,以完成基类部分的拷贝初始化。默认情况下,编译器会自动调用基类的拷贝构造函数,但如果基类有特殊的拷贝构造函数,或者需要特定的初始化逻辑,你可以在派生类的拷贝构造函数中显式调用基类的拷贝构造函数。
#include <iostream>
using namespace std;
class Base {
public:
int data;
Base(int x) : data(x) { // 基类构造函数
cout << "Base class constructor called with value " << x << endl;
}
// 基类拷贝构造函数
Base(const Base& other) : data(other.data) {
cout << "Base class copy constructor called with value " << other.data << endl;
}
};
class Derived : public Base {
public:
int extraData;
Derived(int x, int y) : Base(x), extraData(y) { // 派生类构造函数
cout << "Derived class constructor called with value " << y << endl;
}
// 派生类拷贝构造函数
Derived(const Derived& other) : Base(other), extraData(other.extraData) {
cout << "Derived class copy constructor called with value " << other.extraData << endl;
}
};
int main() {
Derived obj1(10, 20); // 创建派生类对象
Derived obj2 = obj1; // 使用拷贝构造函数
return 0;
}
输出:
Base class constructor called with value 10
Derived class constructor called with value 20
Base class copy constructor called with value 10
Derived class copy constructor called with value 20
当创建 obj1 时,派生类的构造函数首先调用基类的构造函数 Base(x),初始化基类的部分。
当执行 Derived obj2 = obj1; 时,派生类的拷贝构造函数会被调用。在拷贝构造函数中,显式调用了基类的拷贝构造函数 Base(other) 来初始化基类部分,接着派生类成员 extraData 被拷贝。
3.在 C++ 中,当派生类重载 operator= 时,默认情况下会隐藏基类的 operator=,因此如果需要正确地执行基类部分的赋值操作,必须在派生类的赋值运算符中显式调用基类的 operator=。
#include <iostream>
using namespace std;
class Base {
public:
int data;
Base(int x) : data(x) {
cout << "Base class constructor called with value " << x << endl;
}
// 基类赋值运算符
Base& operator=(const Base& other) {
if (this != &other) {
data = other.data;
cout << "Base class assignment operator called with value " << other.data << endl;
}
return *this;
}
};
class Derived : public Base {
public:
int extraData;
Derived(int x, int y) : Base(x), extraData(y) {
cout << "Derived class constructor called with value " << y << endl;
}
// 派生类赋值运算符
Derived& operator=(const Derived& other) {
if (this != &other) {
Base::operator=(other); // 显式调用基类的赋值运算符
extraData = other.extraData;
cout << "Derived class assignment operator called with value " << other.extraData << endl;
}
return *this;
}
};
int main() {
Derived obj1(10, 20); // 创建派生类对象
Derived obj2(30, 40); // 创建另一个派生类对象
obj2 = obj1; // 使用赋值运算符
return 0;
}
输出:
Base class constructor called with value 10
Derived class constructor called with value 20
Base class constructor called with value 30
Derived class constructor called with value 40
Base class assignment operator called with value 10
Derived class assignment operator called with value 20
在派生类 Derived 中,重载了 operator=。当赋值运算符被调用时,派生类首先显式调用基类的赋值运算符 Base::operator=(other),以确保基类部分被正确赋值。
然后,派生类的成员 extraData 被赋值为另一个对象的对应成员 extraData。
4.在 C++ 中,派生类的析构函数会在调用结束后自动调用基类的析构函数,从而确保对象销毁时遵循先销毁派生类成员,再销毁基类成员的顺序。这个机制是通过 C++ 的自动调用机制实现的,即使你没有显式地调用基类的析构函数,它也会被正确地处理。
#include <iostream>
using namespace std;
class Base {
public:
Base() {
cout << "Base class constructor called" << endl;
}
// 基类析构函数
virtual ~Base() {
cout << "Base class destructor called" << endl;
}
};
class Derived : public Base {
public:
Derived() {
cout << "Derived class constructor called" << endl;
}
// 派生类析构函数
~Derived() {
cout << "Derived class destructor called" << endl;
}
};
int main() {
Derived obj; // 创建派生类对象
// 析构函数会在此处自动被调用,先调用派生类析构函数,再调用基类析构函数
return 0;
}
输出:
Base class constructor called
Derived class constructor called
Derived class destructor called
Base class destructor called
当 Derived 类型的对象 obj 被创建时,首先会调用基类 Base 的构造函数,然后调用派生类 Derived 的构造函数。
当对象 obj 生命周期结束时,首先会调用派生类的析构函数,再调用基类的析构函数。这种顺序保证了派生类的成员先被清理。
5.友元关系不能继承,也就是说基类友元不能访问派⽣类私有和保护成员 。
class Student;
class Person
{
public:
friend void Display(const Person& p, const Student& s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl;
cout << s._stuNum << endl;
}
int main()
{
Person p;
Student s;
// 编译报错:error C2248: “Student::_stuNum”: ⽆法访问 protected 成员
// 解决⽅案:Display也变成Student 的友元即可
Display(p, s);
return 0;
}
5. 继承与静态成员
#include <iostream>
using namespace std;
class Person
{
public:
string _name;
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
int main()
{
Person p;
Student s;
//这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的
//说明派⽣类继承下来了,⽗派⽣类对象各有⼀份
cout << &p._name << endl;
cout << &s._name << endl;
//这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的
// 说明派⽣类和基类共⽤同⼀份静态成员
cout << &p._count << endl;
cout << &s._count << endl;
// 公有的情况下,⽗派⽣类指定类域都可以访问静态成员
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
输出:

6. 继承和组合
在 C++ 中,继承(Inheritance)和组合(Composition)是两种不同的面向对象设计方式,它们都有助于实现代码重用和设计结构,但在语义和使用上有所不同。
继承
继承是面向对象编程中的一个重要特性,它允许一个类(子类)继承另一个类(父类)的属性和方法,从而实现代码的复用。继承形成了"是一个"(is-a)关系。
继承的特点:
- 代码重用:子类继承了父类的成员和行为,可以直接使用或重写父类的功能。
- "是一个"关系:继承描述了一个对象属于另一个对象的类型。例如,
Student是Person,Dog是Animal,这体现了"是一个"关系。 - 基类与派生类:派生类继承了基类的属性和方法,但可以扩展或修改它们。
class Person {
public:
Person(const char* name) : _name(name) {}
void introduce() { std::cout << "I am " << _name << std::endl; }
private:
std::string _name;
};
class Student : public Person {
public:
Student(const char* name, int studentId) : Person(name), _studentId(studentId) {}
void study() { std::cout << "Studying with student ID: " << _studentId << std::endl; }
private:
int _studentId;
};
int main() {
Student s("John", 12345);
s.introduce(); // 继承自 Person
s.study(); // Student 特有的行为
}
组合
组合是另一种代码复用机制,它描述了一个类“拥有”另一个类的实例,而不是继承它。组合形成了"有一个"(has-a)关系。
组合的特点:
- 封装性:组合允许我们将复杂的对象分解为多个独立的组件,每个组件负责其自己的功能。
- "有一个"关系:组合描述了一个对象包含其他对象。例如,
Car有一个Engine,House有多个Room,这体现了"有一个"关系。 - 灵活性:可以在类的构造过程中灵活地添加或替换组件。
class Engine {
public:
void start() { std::cout << "Engine starting..." << std::endl; }
};
class Car {
public:
Car() : engine(new Engine()) {} // 组合:Car 拥有一个 Engine
void drive() {
std::cout << "Driving the car." << std::endl;
engine->start(); // 使用组合的 Engine
}
private:
Engine* engine;
};
int main() {
Car car;
car.drive();
}


6879

被折叠的 条评论
为什么被折叠?



