格式:
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,… { <派生类类体> };
继承方式:公有继承(public)
保护继承(protected)
私有继承(private) ----默认
公有单继承
#include<iostream>
#include<string>
class Human { //基类
public:
Human(const std::string& name,int age):m_name(name), m_age(age){}
void eat(const std::string& food) {
std::cout << "我在吃" << food << std::endl;
}
void sleep(int hour) {
std::cout << "我睡了" << hour << "小时" << std::endl;
}
protected://保护成员,在类的内部和子类中可以使用,在外部不能使用
std::string m_name;
int m_age;
};
class Student :public Human { //公有单继承
public:
Student (const std::string&name,int age,int no):Human(name,age),m_no(no){}//子类初始化函数
//需要的参数:包括基类和子类需要初始化的形参
//Human(name,age) 调用基类初始化函数
void who(void) {
std::cout << "我叫" << m_name << ",今年" << m_age << "岁,学号是" << m_no << std::endl;
}
void learn(const std::string& course) {
std::cout << "我在学" << course << std::endl;
}
private:
int m_no;//学号
};
class Teacher :public Human {
public:
Teacher(const std::string& name, int age, int salary) :Human(name, age), m_salary(salary) {}
void who(void) {
std::cout << "我叫" << m_name << ",今年" << m_age << "岁,工资是" << m_salary << std::endl;
}
void teach(const std::string& course) {
std::cout << "我在讲" << course << std::endl;
}
private:
int m_salary;//工资
};
int main()
{
Student s("关羽", 30, 10010);
s.who();
s.eat("牛肉拉面");
s.sleep(6);
s.learn("孙子兵法");
Teacher t("李明", 52, 8000);
t.who();
t.sleep(4);
t.eat("米饭");
t.teach("C++编程");
return 0;
}
向上造型和向下造型:
向上造型:将子类类型的指针或引用转换为基类的指针或引用
向下造型:将基类类型的指针或引用转换为子类的指针或引用
#include<iostream>
#include<string>
class Human {
public:
Human(const std::string& name,int age):m_name(name), m_age(age){}
void eat(const std::string& food) {
std::cout << "我在吃" << food << std::endl;
}
void sleep(int hour) {
std::cout << "我睡了" << hour << "小时" << std::endl;
}
protected:
std::string m_name;
int m_age;
};
class Student :public Human {
public:
Student (const std::string&name,int age,int no):Human(name,age),m_no(no){}
void who(void) {
std::cout << "我叫" << m_name << ",今年" << m_age << "岁,学号是" << m_no << std::endl;
}
void learn(const std::string& course) {
std::cout << "我在学" << course << std::endl;
}
private:
int m_no;
};
int main()
{
Student s("关羽", 30, 10010);
Human* ph = &s; //Student*-->Human* 向上造型
//可访问域缩小了(只能访问基类的成员和成员函数了),这种隐式转换是安全的
//Human*-->Student* 向下造型
//Student* ps = ph; //放大了可访问域,隐式转换非法的
Student* ps = static_cast<Student*>(ph); //向下造型必须显示转换
Human h("张飞", 28);
Student* ps1 = static_cast<Student*> (&h);//不合理的转换
//原本基类中没有的成员变量的数据会混乱
ps1->who(); //学号的输出是乱的
return 0;
}
私有成员的继承
子类继承了基类的私有成员,但是不能直接访问。可以通过公有函数或保护函数进行访问
子类隐藏基类的成员(成员搜索方式)
子类的成员与基类的成员完全同名,此时子类隐藏了基类的成员
成员搜索方式:
先在子类中找,子类中有就不到基类中找了,如果子类中没有,就到基类中找
#include<iostream>
#include<string>
class Base {
public:
void func(void) {
std::cout << "基类func" << std::endl;
}
};
class Derived :public Base {
public:
void func(void) {
std::cout << "子类func" << std::endl;
}
};
int main()
{
Derived d;
d.func(); //子类与基类同名成员,调用子类本身成员
d.Base::func(); //通过子类对象调用基类的同名成员,需要加上基类作用域
return 0;
}
#include<iostream>
#include<string>
class Base {
public:
void func(int i) {
std::cout << "基类func i=" << i << std::endl;
}
void func(int i, int j) {
std::cout << "基类func i+j=" << i+j << std::endl;
}
};
class Derived :public Base {
public:
void func(void) {
std::cout << "子类func" << std::endl;
}
using Base::func;//将基类的成员引入到子类作用域
//这样两个同名函数形成重载关系
};
int main()
{
Derived d;
d.func();
d.func(100);
d.func(100,200);
return 0;
}
继承方式的影响
基类中的成员 | 公有继承的子类 | 保护继承的子类 | 私有继承的子类 |
公有成员 | 公有成员 | 保护成员 | 私有成员 |
保护成员 | 保护成员 | 保护成员 | 私有成员 |
私有成员 | 私有成员 | 私有成员 | 私有成员 |
向上造型不适用的情况
保护继承和私有继承不适用
#include<iostream>
#include<string>
class Base {
public:
void func() {
std::cout << "基类func" <<std::endl;
}
};
class Derived :private Base {
};
int main()
{
Derived d;
Base b = &d; //向上造型 报错
/*
错误原因:子类Derived是私有继承,子类中的成员都变成的私有;在向上造型时,扩大了成员的可访问范围
*/
return 0;
}
子类没有构造函数时注意事项
#include<iostream>
class Base {
public:
Base(void):m_i(0) {
std::cout << "基类无参构造函数" <<std::endl;
}
Base(int i) :m_i(i) {
std::cout << "基类有参构造函数" << std::endl;
}
int m_i;
};
class Derived :public Base {
};
int main()
{
Derived d; //子类没有构造函数时,默认调用基类的无参构造函数来初始化基类子对象
std::cout << d.m_i << std::endl;
//Derived d1(100); 报错--默认只能调用基类的无参构造函数
return 0;
}
子类析构函数
子类析构函数,无论是自定义还是编译器缺省提供的,都会调用基类析构函数来完成基类子对象的销毁
销毁顺序:执行子类析构函数-->析构成员子对象(按声明的逆序)-->执行基类析构函数-->析构基类子对象(按继承表逆序)-->释放内存
向上造型时
#include<iostream>
class Base {
public:
~Base(void) {
std::cout << "基类析构" << std::endl;
}
};
class Derived :public Base {
public:
~Derived(void) {
std::cout << "子类析构" << std::endl;
}
};
int main()
{
Base* d=new Derived;
delete d; //有内存泄漏的风险
//原因:只执行基类析构函数
return 0;
}
解决方法:虚析构函数