C++面向对象编程之继承详解
C++面向对象编程之继承详解
C++中的继承机制是面向对象编程的核心特性之一,它允许一个类(派生类)继承另一个类(基类)的成员变量和成员函数。这种机制不仅能够减少代码冗余,还能实现代码的复用和扩展。本文将从继承的基本概念出发,详细讲解其语法格式、继承方式、隐藏机制、作用域、默认成员函数以及与友元、静态成员的关系等内容。
继承的概念
在现实生活中,继承通常指的是家里的财产或地位传给后代。而在C++中,继承则是一个类(派生类)从另一个类(基类)继承成员变量和成员函数的机制。例如,在一个外卖系统中,骑手、商家和用户都有一些共同的信息(如名字、地址等),通过继承,我们可以将这些共同信息定义在一个基类中,然后让各个派生类继承这个基类,从而避免代码冗余。
继承语法格式
C++中继承的语法格式如下:
class 子类: 继承方式 父类
{
// 内容
};
例如:
class Person
{
public:
void Print()
{
cout << _num << endl;
}
protected:
int _num = 0;
};
class Student : public Person
{
private:
int _id = 3;
};
继承方式
继承方式分为公有(public)、保护(protected)和私有(private)三种。这三种继承方式决定了基类成员在派生类中的访问权限。具体来说:
- 公有继承:基类的公有成员在派生类中保持公有,保护成员保持保护,私有成员保持私有。
- 保护继承:基类的公有成员在派生类中变为保护,保护成员保持保护,私有成员保持私有。
- 私有继承:基类的所有成员在派生类中都变为私有。
隐藏机制
当派生类和基类有同名成员时,会发生隐藏。隐藏机制决定了在派生类中,基类的同名成员将不可见。例如:
class Person
{
public:
void Print()
{
cout << _num << endl;
}
int _num = 0;
};
class Student : public Person
{
public:
int _id = 3;
int _num = 3;
};
int main()
{
Student s;
cout << s._num << endl; // 输出子类的_num
cout << s.Person::_num << endl; // 输出基类的_num
return 0;
}
基类和派生类对象赋值转换
派生类对象可以赋值给基类对象,因为派生类包含了基类的所有成员。但是,基类对象不能赋值给派生类对象,因为基类缺少派生类特有的成员。
继承中的作用域
在继承中,派生类和基类是两个独立的作用域。如果派生类中有与基类同名的成员,那么在派生类中将隐藏基类的同名成员。
派生类的默认成员函数
在继承中,派生类需要处理构造函数、拷贝构造函数、赋值重载和析构函数。例如:
class Person
{
public:
Person(const char* name = "peter") : _name(name) {}
Person(const Person& p) : _name(p._name) {}
Person& operator=(const Person& p)
{
if (this != &p)
_name = p._name;
return *this;
}
~Person() {}
protected:
string _name;
};
class Student : public Person
{
public:
Student(const char* name = "Peter", int id = 0) : Person(name), _id(id) {}
Student(const Student& s) : Person(s), _id(s._id) {}
Student& operator=(const Student& s)
{
if (this != &s)
{
Person::operator=(s);
_id = s._id;
}
return *this;
}
private:
int _id;
};
继承与友元
友元关系不能继承。即使一个类是另一个类的友元,它也不能访问继承链中其他类的私有成员。
继承与静态成员
静态成员属于整个继承链。例如,如果基类有一个静态成员变量,那么在派生类中访问这个变量时,实际上是在访问同一个变量。
菱形继承及菱形虚拟继承
菱形继承是指一个类从两个或多个基类继承相同的内容,这可能导致代码冗余和二义性问题。例如:
class job
{
public:
int _name = 100;
};
class teacher : public job
{
};
class student : public job
{
};
class assistant : public student, public teacher
{
};
int main()
{
assistant a;
cout << a._name << endl; // 编译错误,二义性
return 0;
}
为了解决这个问题,可以使用菱形虚拟继承:
class job
{
public:
int _name = 100;
};
class Student : virtual public job
{
protected:
int _num;
};
class Teacher : virtual public job
{
protected:
int _id;
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse;
};
int main()
{
Assistant a;
cout << a._name << endl; // 正确
return 0;
}
通过使用virtual
关键字,可以确保在继承链中只有一份基类的内容,从而避免冗余和二义性问题。