格式:
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; }
解决方法:虚析构函数