【继承和派生】
引入
继承是面向对象编程的核心特性之一,允许一个类(子类或派生类)基于另一个类(父类或基类)构建,继承父类的属性和方法,同时可以扩展或修改功能(构造函数不能继承)
简言之就是子类继承父类,父类派生出子类。
格式: class 子类名 : 访问修饰符 父类名
如: class ChildClass : public ParentClass
class animal {
public:string tname;int tage;
public:animal(){}animal(string name,int age) {this->tname = name;this->tage = age;}
void printInfo() {cout << "名字叫:" << this->tname << "年龄:" << this->tage << endl;}
};class dog :public animal {
public:char sex;
public:dog(){}dog(int age, string name, char sex) {this->tage = age;this->tname = name;this->sex = sex;}
};int main() {dog d1(2, "wangcai", 'm');d1.printInfo();return 0;
}
因为dog类继承了animal类,所以在dog类里面可以直接拥有animal里面的tname和tage以及printInfo,同时又可以自己添加成员变量、成员函数。
三种继承方式
根据自己的理解记忆,我是按着public限制最少、优先级最低、最容易被修改,protected占中位,private优先级最高。
比如子类public继承
- 父类中访问修饰词是public时,子类也public;
- 父类protected时,因优先级比public高,则子类不修改(仍为protected)。
子类protected继承 - 父类中访问修饰词是public时,子类继承的优先级更高,改成protected;
- 父类protected时,子类也protected。
子类private继承 - 父类中访问修饰词是public或protected时,子类继承的优先级更高,均需要改成private;
所有父类private成员在子类中都不能使用。
如图,当修改printInfo的访问修饰符为private后再引用就报错了
所说的子类的不同继承方式是由上文格式部分的访问修饰符决定的。
也就是圈住的部分:
完整代码:
class person {
public:string tname;
protected:int tage;
private:char tsex;public:person(){}
void setName(string name) {this->tname = name;}
void setAge(int age) {this->tage = age;
}void setSex( char sex) {this->tsex = sex;
}void printInfo() {cout << "名字叫:" << this->tname << " 年龄" << this->tage << endl;}
};class student :public person {};int main() {student s1;s1.setName("aaa");s1.setAge(10);s1.setSex('g');s1.printInfo();return 0;
}
tage被protected修饰,在列表中没有tage,不能直接访问
但能在派生类(子类)中使用,只有private既不能直接访问又不能在派生类中使用。
public:person(){}person(string name, int age, char sex) :tname(name), tage(age), tsex(sex){}/*person(string name, int age, char sex) {this->tname = name;this->tage = age;this->tsex = sex;}*/
这两种(注释掉的部分和注释的上一行)带参数的构造函数效果是一样的(下面的展示就把注释的那种方法删掉了)。
完整代码:
class person {
public:string tname;
protected:int tage;
private:char tsex;public:person(){}person(string name, int age, char sex) :tname(name), tage(age), tsex(sex){}void printInfo() {cout << "名字叫:" << this->tname << " 年龄" << this->tage << endl;}
};class student :public person {};int main() {person s1("aa", 10, 'b');s1.printInfo();return 0;
}
前面提到过父类的构造函数不能继承给子类,但子类可以引用父类的构造函数。
子类可以引用父类的构造函数
方法一:
class person {
public:string tname;
protected:int tage;
private:char tsex;public:person(){}person(string name, int age, char sex) :tname(name), tage(age), tsex(sex){}void printInfo() {cout << "名字叫:" << this->tname << " 年龄" << this->tage << endl;}
};class student :public person {
public:double score;
public:student(){}//新增变量分开写student(string name, int age, char sex, double score) :person(name, age, sex), score(score){}void showInfo() {cout << "名字:" << this->tname << "年龄:" << this->tage << endl;}
};int main() {student s1;student s2("aaa",10,'a',11.1);s2.printInfo();return 0;
}
方法二:通过using来继承基类构造函数
格式: using 基类::基类;
class person {public:person(){}person(string name, int age, char sex) :tname(name), tage(age), tsex(sex){}void printInfo() {cout << "名字叫:" << this->tname << " 年龄" << this->tage << endl;}
public:string tname;
protected:int tage;
private:char tsex;
};class student :public person {
public:using person::person;student() {}student(string name, int age, char sex, double score) :person(name, age, sex), score(score) {}void showInfo() {cout << "名字:" << this->tname << "年龄:" << this->tage << endl;}
private:double score;
};int main() {student stu("aaa",10,'a',11.1);stu.printInfo();return 0;
}
这里与上面的代码只加了一行using person::person;不能体现出using的作用,需参考下面:
class person {
public:person() {}person(string name) : tname(name) {}person(string name, int age) : tname(name), tage(age) {}person(string name, int age, char sex) : tname(name), tage(age), tsex(sex) {}person(int age) : tage(age) {}// ... 更多构造函数
};class student : public person {
public:using person::person; // 一行代码继承所有5个构造函数// 只需要处理派生类特有的情况student(string name, int age, char sex, double score) : person(name, age, sex), score(score) {}
};
构造函数调用顺序
using namespace std;class animal {
public:animal() {cout << "call animal " << endl;}
};class monkey :public animal {
public:monkey() {cout << "call monkey " << endl;}
};int main() {monkey m1;return 9;
}
输出结果:先调用父类,后调用子类
- 子类继承父类时实例化子类,如果不知道基类构造函数就用默认构造函数,没有就会报错。
(默认构造函数无参数,所以很多时候会留一个空白的构造函数,如:people(){ }; 再自定义有参数的构造函数。)
析构函数调用顺序
class animal {
public:animal() {cout << "call animal " << endl;}~animal() {cout << "call ~animal " << endl;}
};class monkey :public animal {
public:monkey() {cout << "call monkey " << endl;}~monkey() {cout << "call ~monkey " << endl;}
};int main() {monkey m1;return 0;
}
输出: