继承与派生
继承可以理解为一个类从另一个类获取成员变量和成员函数的过程,被继承的类称为基类,继承的类称为派生类。
本文目录(3 节)
1. 继承
继承可以理解为一个类从另一个类获取成员变量和成员函数的过程,被继承的类称为基类,继承的类称为派生类。
派生类除了拥有基类成员,还可以自定义新成员。
1.1 继承的一般形式
class 派生类名:[继承方式] 基类名{
//派生类新增成员;
};
-
示例
#include<iostream> using namespace std; //基类 People class People{ public: void setname(char *name); void setage(int age); char *getname(); int getage(); private: char *m_name; int m_age; }; void People::setname(char *name){ m_name = name; } void People::setage(int age){ m_age = age; } char* People::getname(){ return m_name; } int People::getage(){ return m_age;} //派生类 Student class Student: public People{ //Student类继承People类,public方式; public: void setscore(float score); float getscore(); private: float m_score; }; void Student::setscore(float score){ m_score = score; } float Student::getscore(){ return m_score; } int main(){ Student stu; stu.setname("小明"); stu.setage(16); stu.setscore(95.5f); cout<<stu.getname()<<"的年龄是 "<<stu.getage()<<",成绩是 "<<stu.getscore()<<endl; return 0; }
1.2 继承方式
有三种:public(公有)、private(私有)和 protected(受保护),默认 private。
类成员的访问权限:public > protected > private
其中,protected 成员和 private 成员类似,也不能通过对象访问;但是在继承的时候,基类的 protected 成员可以在派生类中使用,但是 private 成员不行。
2.1 继承方式对成员的影响
| 继承方式/基类成员 | public成员 | proteced成员 | private成员 |
|---|---|---|---|
| public 继承 | public | proteced | 不可见 |
| proteced 继承 | proteced | proteced | 不可见 |
| private 继承 | private | private | 不可见 |
2. 继承中的名字遮蔽
如果派生类中的成员和基类中的成员重名,那么会遮蔽从基类继承过来的成员,即使用该成员时实际上使用的是派生类中的成员。
因此,基类和派生类中的同名函数不构成重载。
例如:
``` C++
#include<iostream>
using namespace std;
//基类Base
class Base{
public:
void func();
void func(int);
};
void Base::func(){ cout<<"Base::func()"<<endl; }
void Base::func(int a){ cout<<"Base::func(int)"<<endl; }
//派生类Derived
class Derived: public Base{
public:
void func(char *);
void func(bool);
};
void Derived::func(char *str){ cout<<"Derived::func(char *)"<<endl; }
void Derived::func(bool is){ cout<<"Derived::func(bool)"<<endl; }
int main(){
Derived d;
d.func("hello C++");
d.func(true);
// d.func(); //compile error
// d.func(10); //compile error
d.Base::func();
d.Base::func(100);
return 0;
}
```
上例中,构成重载的只是 Base 类的两个 func 或者 Derive 类的两个 func。
所以要是想访问基类的同名函数,需要指定作用域,如 d.Base::func();、d.Base::func(100); 两行所示。
3. 派生类的构造函数
类的构造函数不能被继承,所以在初始化基类的成员变量时,是在派生类的构造函数中调用基类的构造函数来完成初始化。
#include<iostream>
using namespace std;
//基类People
class People{
protected:
char *m_name;
int m_age;
public:
People(char*, int);
};
People::People(char *name, int age): m_name(name), m_age(age){}
//派生类Student
class Student: public People{
private:
float m_score;
public:
Student(char *name, int age, float score);
void display();
};
//People(name, age)就是调用基类的构造函数
Student::Student(char *name, int age, float score): People(name, age), m_score(score){ }
void Student::display(){
cout<<m_name<<"的年龄是"<<m_age<<",成绩是"<<m_score<<"。"<<endl;
}
int main(){
Student stu("小明", 16, 90.5);
stu.display();
return 0;
}
People(name, age) 就是调用基类的构造函数,并将 name 和 age 作为实参传递给它;
m_score(score) 是派生类的参数初始化列表,它们之间用逗号隔开。
注:多层派生中,派生类的构造函数中只能调用直接基类的构造函数,不能调用间接基类的。
同理,析构函数也是不能被继承的,并且因为每个类只有一个析构函数,所以派生类的析构函数不需要显式地调用基类的构造函数,编译器会自动选择。
另外,析构函数的执行顺序和构造函数的执行顺序也刚好相反:
-
创建派生类对象时,构造函数的执行顺序和继承顺序相同,即先执行基类构造函数,再执行派生类构造函数。
-
而销毁派生类对象时,析构函数的执行顺序和继承顺序相反,即先执行派生类析构函数,再执行基类析构函数。
4. C++指针可以突破权限限制
万能的指针是可以穿透地址,随意修改类的成员,即使是 private、const 成员也无济于事。
5. C++向上转型
将派生类赋值给基类
C/C++ 中对不同类型的变量进行赋值时,编译器会将其转化成同类型,然后再赋值。
同理,类也是一种数据类型,也可以进行数据转化,这种转化一般只发生在基类和派生类之间。
将派生类赋值给基类称为向上转型,将基类赋值给派生类称为向下转型。
向上转型很安全,由编译器完成,向下转型有风险,需要程序员手动干预。
#include <iostream>
using namespace std;
//基类
class A{
public:
A(int a);
void display();
int m_a;
};
A::A(int a): m_a(a){ }
void A::display(){
cout<<"Class A: m_a="<<m_a<<endl;
}
//派生类
class B: public A{
public:
B(int a, int b);
void display();
int m_b;
};
B::B(int a, int b): A(a), m_b(b){ }
void B::display(){
cout<<"Class B: m_a="<<m_a<<", m_b="<<m_b<<endl;
}
int main(){
A a(10);
B b(66, 99);
//赋值前
a.display();
b.display();
cout<<"--------------"<<endl;
//赋值后
a = b;
a.display();
b.display();
return 0;
}
- 运行结果:
Class A: m_a=10
Class B: m_a=66, m_b=99
----------------------------
Class A: m_a=66
Class B: m_a=66, m_b=99
将派生类对象赋值给基类对象时,会舍弃派生类新增的成员:

注:同一基类的不同派生类对象之间也不能赋值
本文可自由转载与修改,但需保留作者署名与出处链接,不得用于商业用途,且衍生作品需以相同方式共享。 转载请注明:MacLodge's Blog · 继承与派生