
我们知道类和对象中存在6个默认成员函数,默认的意思就是指我们不写,编译器会变我们自动生成⼀个,那么在派生类中,这几个成员函数是如何生成的呢?
#include<iostream>
#include<string>
uisng namespace std;
class Person
{
public:
Person(const char* name)
:_name(name)
{
cout << "Person()基类构造" << endl;
}
protected:
string _name;
};
class Student :public Person
{
public:
//===============================
//派生类的构造 与类和对象的规则高度相似
//但是要分开两部份
//派生类中的 基类->调用父类的构造
//派生类中的 特有成员->调用派生类自己的构造
//====================================
Student(const char* name,int num,int age)
//:_name(name) 这里不能这样写 派生类的构造函数必须调用基类的构造函数来初始化基类的那一部分成员
:Person(name)
,_num(num)
,_age(age)
{
cout<<"Student(const char* name,int num,int age)派生类构造"<<endl;
}
protected:
int _num;
int _age;
};
int main()
{
Student s("张三", 2416010101, 18);
}

注意:
#include<iostream>
#include<string>
uisng namespace std;
class Person
{
public:
Person(const char* name)
:_name(name)
{
cout << "Person()基类构造" << endl;
}
Person(const Person& p)
:_name(p._name)
{
cout << "Person(const Person& p)基类的拷贝构造" << endl;
}
protected:
string _name;
};
class Student :public Person
{
public:
Student(const char* name,int num,int age)
:Person(name)
,_num(num)
,_age(age)
{
cout<<"Student(const char* name,int num,int age)派生类构造"<<endl;
}
Student(const Student& s)
//把派生类中的基类看作是一个整体 调用基类的拷贝构造
:Person(s)//传的是一个Person类对象
,_num(s._num)
,_age(s._age)
{
cout << "Student(const Student& s)派生类的拷贝构造" << endl;
}
protected:
int _num;
int _age;
};
int main()
{
Student s("张三", 2416010101, 18);
Student s2(s);
}

注意:
class Person
{
public:
Person(const char* name)
:_name(name)
{
cout << "Person()基类构造" << endl;
}
Person(const Person& p)
:_name(p._name)
{
cout << "Person(const Person& p)基类的拷贝构造" << endl;
}
//赋值重载
Person& operator=(const Person& s)
{
if (&s != this)
{
_name = s._name;
}
return *this;
}
protected:
string _name;
};
class Student :public Person
{
public:
Student(const char* name,int num,int age)
:Person(name)
,_num(num)
,_age(age)
{
cout << "Student(const char* name,int num,int age)派生类构造" << endl;
}
Student(const Student& s)
:Person(s)
,_num(s._num)
,_age(s._age)
{
cout << "Student(const Student& s)派生类的拷贝构造" << endl;
}
//赋值重载
Student& operator=(const Student& s)
{
if (&s != this)
{
//基类部分调用基类的赋值重载 而由于基类的operator=()与派生类的operator=()构成隐藏
//所以在调用基类的operator=()时要指定类域
Person::operator=(s);
_num = s._num;
_age = s._age;
}
return* this;
}
protected:
int _num;
int _age;
};
int main()
{
Student s("张三", 01, 18);
cout << endl;
Student s2(s);
cout<<endl;
Student s3("李四", 2, 25);
s3 = s2;
}

注意:
(operator=)必须显式调用基类的赋值运算符来完成基类部分的复制。需要注意的是,派生类的operator=会隐藏基类的operator=,因此在调用时需通过基类作用域来明确指定。class Person
{
public:
Person(const char* name)
:_name(name)
{
cout << "Person()基类构造" << endl;
}
Person(const Person& p)
:_name(p._name)
{
cout << "Person(const Person& p)基类的拷贝构造" << endl;
}
Person& operator=(const Person& s)
{
if (&s != this)
{
_name = s._name;
}
return *this;
}
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name;
};
class Student :public Person
{
public:
Student(const char* name, int num, int age)
:Person(name)
, _num(num)
, _age(age)
{
cout << "Student(const char* name,int num,int age)派生类构造" << endl;
}
Student(const Student& s)
:Person(s)
, _num(s._num)
, _age(s._age)
{
cout << "Student(const Student& s)派生类的拷贝构造" << endl;
}
Student& operator=(const Student& s)
{
if (&s != this)
{
Person::operator=(s);
_num = s._num;
_age = s._age;
}
return*this;
}
//析构函数名字因为后续多态(重写)章节原因,会被处理成destructor
//所以派生类和基类析构构成隐藏关系
~Student()
{
//Person::~Person();这行代码可以不用写,因为~Student本身会自动调用
} // 自动调用父类析构, 才能保证先子后父的析构顺序
// 派生类析构调用后,会自动调用父类析构,所以自己实现析构时不需要显示调用
// 构造初始化,先父类后子。析构清理资源,先子后父。
protected:
int _num;
int _age;
};
int main()
{
Student s("张三", 01, 18);
cout << endl;
Student s2(s);
cout << endl;
Student s3("李四", 2, 25);
s3 = s2;
}注意:
destructor(),所以基类析构函数不加virtual的情况下,派生类析构函数和基类析构函数构成隐藏关系。派生类默认成员函数行为总结:

友元关系不能继承,也就是说基类友元不能访问派⽣类私有和保护成员 。
//前置声明
class Student;
//基类
class Person
{
public:
//友元函数
friend void Display(const Person& p, const Student& s);//这里用到了Student当参数
//编译器向上找找不到会报错 所以要在上面加上前置声明
protected:
string _name; // 姓名
private:
int _a;
};
//派生类
class Student : public Person
{
public:
//把Display函数也声明
friend void Display(const Person& p, const Student& s);
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;
}
注意友元关系是不能继承的,将
Display声明成Person的友元,则在Display中可以访问Person中的私有成员。子类Student继承了父类Person后,并不能使用Display函数,因为Display是Person的友元而不是Student的友元。就相当于你父亲的朋友不一定是你的朋友一样。解决方法:在Student里也声明友元。
基类中定义的静态成员在整个继承体系中是唯一的。无论派生出多少个子类,该静态成员始终只有一个static实例存在。
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;
}
继承(is-a)与组合(has-a)对比表格
特性 | 继承(is-a) | 组合(has-a) |
|---|---|---|
关系类型 | is-a 关系,派生类是基类的一种类型 | has-a 关系,对象包含另一个对象 |
复用类型 | 白箱复用:派生类可访问基类实现细节 | 黑箱复用:对象细节不可见,仅通过接口访问 |
耦合度 | 高,基类变化可能影响派生类 | 低,改变一个对象不会影响另一个对象 |
优点 | 代码复用,支持多态和功能扩展 | 类间关系松散,低耦合,易于维护和替换 |
缺点 | 破坏封装,强依赖性,基类变动风险 | 需定义清晰接口,可能增加代码量 |
使用场景 | 类间存在逻辑层级关系且需多态支持(如动物→狗) | 类间需功能组合但无层级关系(如汽车→发动机) |
📢继承类型:公有继承保持基类访问权限,保护继承降级为保护成员,私有继承降级为私有成员。
📢构造与析构:派生类构造函数隐式调用基类默认构造,显式调用需通过初始化列表;析构顺序与构造相反。
📢访问控制:派生类可访问基类公有和保护成员,外部对象仅能访问公有成员(公有继承时)。
📢继承与组合:继承体现“is-a”关系(特化),组合体现“has-a”关系(包含),需根据设计目标选择。