原创作者:郑同学的笔记
原创地址:https://zhengjunxue.blog.csdn.net/article/details/131795289
qq技术交流群:921273910
C++ 是基于面向对象的程序,面向对象有三大特性 —— 封装、继承、多态。
#include
#include
using namespace std;
class Person {
/* 共有的信息 */
public:
void print()
{
cout << "name = "<<m_name << "\nage = "<<m_age << endl;
}
string m_name;
int m_age;
};
/* Student 公有继承了 Person */
class Student : public Person {
string m_stuID; // 学号
};
/* Teacher 公有继承了 Person */
class Teacher : public Person {
string m_employeeID; // 工号
};
int main()
{
Person p;
p.print();
Student su;
su.print();
Teacher t;
t.print();
return 0;
}
输出
class 派生类名:[继承方式] 基类名{
派生类新增加的成员
};
下表汇总了不同继承方式对不同属性的成员的影响结果
继承方式/基类成员 | public成员 | protected成员 | private成员 |
---|---|---|---|
public继承 | public | protected | 不可见 |
protected继承 | protected | protected | 不可见 |
private继承 | private | private | 不可见 |
由于 private 和 protected 继承方式会改变基类成员在派生类中的访问权限,导致继承关系复杂,所以实际开发中我们一般使用 public。
#include
#include
using namespace std;
class Person {
/* 共有的信息 */
public:
void print()
{
cout << "name = "<<m_name << "\nage = "<<m_age << endl;
}
string m_name;
int m_age;
};
/* Student 公有继承了 Person */
class Student : public Person {
string m_stuID; // 学号
};
int main()
{
Student s;
Person p = s;
Person* pointer_p = &s;
Person& ref_p = s;
p.print();
pointer_p->print();
ref_p.print();
return 0;
}
输出
注意事项:
Student s; // 子类
Person p; // 父类
s = p; ❌
这里父类如果是多态类型,可以使用 RTTI(Run-Time Type Information,即运行时类型识别)的 dynamic_cast 来进行识别后进行安全转换。
#include
#include
using namespace std;
class Person {
/* 共有的信息 */
public:
void print()
{
cout << "name = "<<m_name << "\nage = "<<m_age << endl;
}
string m_name;
int m_age;
};
/* Student 公有继承了 Person */
class Student : public Person {
public:
string m_stuID; // 学号
};
int main()
{
Student s;
// 父类的指针可以通过强制类型转换赋值给子类的指针
Person* pointer_p = &s;
Student* pointer_s = (Student*)pointer_p;
pointer_s->m_stuID;
Person p;
// 这种情况虽然可以,但是会存在越界访问问题
Person* pointer_p2 = &p;
Student* pointer_s2 = (Student*)pointer_p2;
pointer_s->m_stuID;
return 0;
}
在子类成员函数中,可以使用如下方式进行显式访问:
基类::基类成员
–
例如:在Student类中
Person::print()
#include
#include
using namespace std;
class Person {
/* 共有的信息 */
public:
void print()
{
cout << "name = "<<m_name << "\nage = "<<m_age << endl;
}
string m_name;
int m_age;
};
/* Student 公有继承了 Person */
class Student : public Person {
public:
void print()
{
cout << "name = " << m_name << "\nage = " << m_age <<"\nstuID = "<< m_stuID << endl;
}
string m_stuID; // 学号
};
int main()
{
Student s;
s.print();
return 0;
}
#include
#include
using namespace std;
class Student;
class Person {
public:
friend void Display(const Person& p, const Student& s);
/* 共有的信息 */
protected:
void print()
{
cout << "name = "<<m_name << "\nage = "<<m_age << endl;
}
string m_name;
int m_age;
};
/* Student 公有继承了 Person */
class Student : public Person {
protected:
void print()
{
cout << "name = " << m_name << "\nage = " << m_age <<"\nstuID = "<< m_stuID << endl;
}
string m_stuID = "110"; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p.m_name << endl;
cout << s.m_stuID << endl;
}
int main()
{
Person p;
Student s;
Display(p, s);
return 0;
}
报错
“Student::m_stuID”: 无法访问 protected 成员(在“Student”类中声明)
可以理解为共享,父类的静态成员可以在子类共享,父类和子类都能去访问它。
无论派生出多少个子类,都只有一个 static 成员实例:
#include
#include
using namespace std;
class Person {
public:
Person() {
++m_count;
}
void print()
{
cout << "name = "<<m_name << "\nage = "<<m_age << endl;
}
protected:
string m_name;
int m_age;
public:
static int m_count;
};
int Person::m_count = 0;
/* Student 公有继承了 Person */
class Student : public Person
{
protected:
string m_stuID = "110"; // 学号
};
int main()
{
Student s1;
Student s2;
Student s3;
Person s;
cout << "大家都可以访问" << endl;
cout << "人数 : " << Person::m_count << endl;
cout << "人数 : " << Student::m_count << endl;
cout << "大家也都可以变动" << endl;
s3.m_count = 0;
cout << "人数 : " << Person::m_count << endl;
cout << "并且他们的地址也都是一样的,因为所有继承体系中只有一个" << endl;
cout << "人数 : " << &Person::m_count << endl;
cout << "人数 : " << &Student::m_count << endl;
return 0;
}
输出
如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。(如下面的demo)
#include
#include
using namespace std;
class Person {
public:
Person(const char* m_name = "hello") {
cout<<"构造 Person \n";
}
protected:
string m_name;
int m_age;
};
class Student : public Person
{
public:
Student(const char* name, int stuID)
:Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。
m_stuID(stuID)
{
cout << "构造 Student \n";
}
protected:
int m_stuID; // 学号
};
int main()
{
Student s1("",18);
return 0;
}
思考:如何设计一个不能被继承的类?
class A
{
private:
A() {}
};
父类的构造函数私有化后,在父类的外部,父类自己也没法初始化了?
(单例模式可以这么做,如下)
class A {
public:
static A CreateObject() { // 提供一个获取对象的方式
return A();
}
private:
A() {}
};
class B : public A {};
int main(void)
{
A a = A::CreateObject();
return 0;
}
#include
#include
using namespace std;
class Person {
public:
Person(const char* m_name = "hello") {
cout<<"构造 Person \n";
}
Person(const Person& p)
:m_name(p.m_name)
{
cout << "拷贝构造 Person \n";
}
protected:
string m_name;
int m_age;
};
class Student : public Person
{
public:
Student(const char* name, int stuID)
:Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。
m_stuID(stuID)
{
cout << "构造 Student \n";
}
Student(const Student& s)
:Person(s), //子类的拷贝构造函数必须调用父类的拷贝构造完成拷贝初始化。
m_stuID(s.m_stuID)
{
cout << "拷贝构造 Student \n";
}
protected:
int m_stuID; // 学号
};
int main()
{
Student s1("haha",18);
Student s2(s1);
return 0;
}
输出
#include
#include
using namespace std;
class Person {
public:
Person(const char* m_name = "hello") {
cout<<"构造 Person \n";
}
Person& operator=(const Person& p)
{
cout << "赋值重载 Person \n";
if(this != &p)
{
m_name = p.m_name;
}
return *this;
}
protected:
string m_name;
int m_age;
};
class Student : public Person
{
public:
Student(const char* name, int stuID)
:Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。
m_stuID(stuID)
{
cout << "构造 Student \n";
}
Student& operator=(const Student& s)
{
cout << "赋值重载 Student \n";
if (this != &s)
{
Person::operator=(s); //子类的 operator= 必须要调用父类的 operator= 完成父类的复制。
m_stuID = s.m_stuID;
}
return *this;
}
protected:
int m_stuID; // 学号
};
int main()
{
Student s1("小白",18);
Student s2("小黑", 18);
s1 = s2;
return 0;
输出
子类析构先子后父,子类对象的析构清理是先调用子类析构再调父类析构。
#include
#include
using namespace std;
class Person {
public:
Person(const char* m_name = "hello") {
cout<<"构造 Person \n";
}
~Person()
{
cout << "析构 Person \n";
}
protected:
string m_name;
int m_age;
};
class Student : public Person
{
public:
Student(const char* name, int stuID)
:Person(name), //如果 父类没有默认的构造函数,则必须在子类构造函数的初始化列表阶段显式调用。
m_stuID(stuID)
{
cout << "构造 Student \n";
}
~Student()
{
cout << "析构 Student \n";
}
protected:
int m_stuID; // 学号
};
int main()
{
Student s1("小白",18);
return 0;
}
输出
下面是一个示例代码来说明菱形继承的概念:
class Animal {
public:
void eat() {
cout << "Animal eats." << endl;
}
};
class Mammal : public Animal {
public:
void run() {
cout << "Mammal runs." << endl;
}
};
class Bird : public Animal {
public:
void fly() {
cout << "Bird flies." << endl;
}
};
class Bat : public Mammal, public Bird {
public:
void sleep() {
cout << "Bat sleeps." << endl;
}
};
在上述代码中,Animal 是基类,Mammal 和 Bird 是直接派生类,而 Bat 是通过多重继承同时派生自 Mammal 和 Bird 的派生类。注意到 Mammal 和 Bird 都继承自 Animal,这就形成了菱形继承结构。
class Mammal : virtual public Animal {
// ...
};
class Bird : virtual public Animal {
// ...
};
class Bat : public Mammal {
private:
Bird bird;
public:
// 使用 bird 对象来访问 Bird 类中的成员
};
菱形继承是多重继承中的一种特殊情况,需要谨慎使用,并采取适当的解决方案来避免引发问题。
class A {
// ...
};
// 继承
class B : public A {};
class C {
// ...
};
// 组合
class D {
C _c;
};
继承就是团体出行,A 任何成员的修改都有可能影响 B 的实现。
组合就是自由出行,C 只要不修改公有,就不会对 D 有影响。