继承是面向对象编程的一个重要概念,他允许一个类(称为子类或者派生类)从另一个类(称为分类或者基类)继承属性和行为,这使得代码重用更为容易。 继是类设计层次的复用。
C++简单的继承示例
//基类
class Animal
{
public:
//构造函数
Animal(const string & name = "大黄") :_name(name) {}
void sleep()
{
cout << _name << "is sleeping" << endl;
}
protected:
string _name;
};
//派生类
class Dog :public Animal
{
public:
Dog()
{
_breed = "土狗";
}
void print()
{
cout << _name << endl;//派生类继承基类的_name属性
cout << _breed << endl;
}
private:
string _breed;
};
int main()
{
Dog d1;//创建派生类对象。
d1.sleep();//继承基类的成员函数
d1.print();//调用自己的成员函数
return 0;
}
public
继承是最常见的形式,它使得基类的公共成员在派生类中保持为公共成员,基类的受保护成员在派生类中保持为受保护成员。class Base {
public:
int _publicMember;
protected:
int _protectedMember;
private:
int _privateMember;
};
class Derived : public Base {
// _publicMember 在 Derived 中仍然是公共的
// _protectedMember 在 Derived 中仍然是受保护的
// _privateMember 对于 Derived 是不可见的不可访的
};
protected
继承使得基类的公共和受保护成员在派生类中变成受保护的。class Base {
public:
int _publicMember;
protected:
int _protectedMember;
private:
int _privateMember;
};
class Derived : protected Base {
// _publicMember 在 Derived 中变成受保护的
// _protectedMember 在 Derived 中仍热是受保护的
// _privateMember 对于 Derived 是不可见的
};
private
继承使得基类的公共和受保护成员在派生类中变成私有的。class Base {
public:
int _publicMember;
protected:
int ——protectedMember;
private:
int _privateMember;
};
class Derived : private Base {
// _publicMember 在 Derived 中变成私有的
// _protectedMember 在 Derived 中变成私有的
// _privateMember 对于 Derived 是不可见的
};
class Base {
//...
};
//class默认是private继承
class Derived : Base {
//...
};
//struct默认是public继承
struct Derived : Base {
//...
};
使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过最好显示的写出继承方式。
- 一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡 使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里 面使用,实际中扩展维护性不强。
基类::基类成员 显示访问
class Person
{
protected:
string _name = "张三";//姓名
string _sex = "男";//性别
int _age = 18;//年龄
int _num = 13456789;//身份证号
};
class Student : public Person
{
public:
void print()
{
cout << _name << endl;
cout << _sex << endl;
cout << _age << endl;
cout << _num << endl;//屏蔽基类直接访问自己的成员变量
}
private:
int _num = 666;//学号
};
void print()
{
cout << _name << endl;
cout << _sex << endl;
cout << _age << endl;
cout << _num << endl;//屏蔽基类直接访问自己的成员变量
cout << Person::_num << endl;//指定作用域访问基类的成员
}
class Person
{
public:
void print()
{
cout << _name << endl;
cout << _sex << endl;
cout << _age << endl;
cout << _num << endl;
}
protected:
string _name = "张三";//姓名
string _sex = "男";//性别
int _age = 18;//年龄
int _num = 13456789;//身份证号
};
class Student : public Person
{
public:
void print()
{
cout << _name << endl;
cout << _sex << endl;
cout << _age << endl;
cout << _num << endl;
cout << Person::_num << endl;
}
private:
int _num = 666;//学号
};
int main()
{
Student s1;//派生类对象
s1.print();//调用自己的成员
s1.Person::print();//指定作用域,调用基类的成员函数
return 0;
}
//基类
class Person
{
public:
Person(string name = "张三", string sex = "男", int age = 18)
:_name(name)
,_sex(sex)
,_age(age)
{}
void print()
{
cout << "name = " << _name << endl;
cout << "sex = " << _sex << endl;
cout << "age = " << _age << endl;
}
protected:
string _name;//姓名
string _sex;//性别
int _age;//年龄
};
//派生类
class Student : public Person
{
public:
Student()
:Person()//调用基类的默认构造
,_id(1111)//初始化自己的成员变量
{}
void print()
{
Person::print();调用基类的成员函数
cout << "id = " << _id << endl;
}
private:
int _id;//学号
};
int main()
{
Student s1;//派生类对象
s1.print();
Person p1 = s1;//派生类对象赋值给基类对象
p1.print(); //基类对象访问基类成员函数
Person& pp = s1;//派生类对象赋值给基类引用对象
pp.print();//基类引用对象访问基类成员函数
Person* ptrp = &s1;//派生类对象赋值给基类指针对象
ptrp->print();//基类指针对象访问基类成员函数
//基类对象不能赋值给派生类对象
//s1 = p1;//error
return 0;
}
将派生类中属于基类的一部分切割出来赋值给基类对象
6个默认成员函数,意思就是指我们不写,编译器会默认生成,在派生类中,他们的默认成员函数和之前的会有差别
基类有默认构造函数:
//基类
class Person
{
public:
Person()
{
cout << "Person()" << endl;
}
protected:
string _name;//姓名
};
//派生类 派生类中没有实现构造函数
class Student : public Person
{
public:
private:
int _id;//学号
};
int main()
{
Student s1;//s1调用基类的构造函数
return 0;
}
运行结果:
_id会为随机值,编译器默认生成的构造函数对内置类型不作处理(有些编译器会初始化为0)
基类没有默认构造函数
//基类
class Person
{
public:
Person(const char* name)
:_name(name)
{
cout << "Person(const char* name)" << endl;
}
protected:
string _name;//姓名
};
//派生类
class Student : public Person
{
public:
Student(const char* name, int id = 1111)
:Person(name)//必须在派生类的构造函数的初始化列表中显示调用 并传递必要的参数
, _id(id)
{
cout << "Student(const char* name, int id = 1111)" << endl;
}
private:
int _id;//学号
};
int main()
{
Student s1("张三");
return 0;
}
运行结果:
//基类
class Person
{
public:
//基类构造函数
Person(const char* name)
:_name(name)
{
cout << "Person(const char* name)" << endl;
}
//基类拷贝构造函数
Person(const Person& person)
:_name(person._name)
{
cout << "Person(const Person& person)" << endl;
}
protected:
string _name;//姓名
};
//派生类
class Student : public Person
{
public:
//派生类构造函数
Student(const char* name,int id = 1111)
:Person(name)
,_id(id)
{
cout << "Student(const char* name,int id = 1111)" << endl;
}
//派生类拷贝构造函数
Student(const Student& student)
:Person(student)//派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化
,_id(student._id)//在赋值派生类自己的一部分
{
cout << "Student(const Student& student)" << endl;
}
private:
int _id;//学号
};
int main()
{
Student s1("张三");
Student s2(s1);
return 0;
}
运行结果:
//基类
class Person
{
public:
//基类构造函数
Person(const char* name)
:_name(name)
{
cout << "Person(const char* name)" << endl;
}
//基类析构函数
~Person()
{
cout << "~Person()" << endl;
}
protected:
string _name;//姓名
};
//派生类
class Student : public Person
{
public:
//派生类构造函数
Student(const char* name,int id = 1111)
:Person(name)
,_id(id)
{
cout << "Student(const char* name,int id = 1111)" << endl;
}
//派生类析构函数
~Student()
{
cout << "~Student()" << endl;
}
private:
int _id;//学号
};
int main()
{
Student s1("张三");
return 0;
}
运行结果:
派生类调用析构完成后会自动调用基类析构函数
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员
//声明Student类
class Student;
class Person
{
public:
//友元函数声明 是Person的友元
friend void Display(const Person& p, const Student& s);
protected:
string _name; // 姓名
};
class Student : public Person
{
protected:
int _stuNum; // 学号
};
void Display(const Person& p, const Student& s)
{
cout << p._name << endl; //可以访问Person的保护成员
cout << s._stuNum << endl; //不可以访问
}
int main()
{
Person p;
Student s;
Display(p, s);
return 0;
}
class Person
{
public:
Person()
{
++_count;
}
static int _count;//统计人数个数
protected:
string _name;//姓名
};
int Person::_count = 0;
class Student : public Person
{
public:
Student(int id = 1)
:Person()
,_id(id)
{}
protected:
int _id;//学号
};
class Teacher : public Person
{
public:
Teacher(int no = 1)
:Person()
, _no(no)
{}
protected:
int _no;//编号
};
int main()
{
Student s1;
Student s2;
Teacher t1;
Teacher t2;
cout << Person::_count << endl;//4
return 0;
}
单继承:一个子类只有一个直接父类时称这个继承关系为单继承
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
菱形继承:菱形继承是多继承的一种特殊情况
比如
Assistant类继承了Student和Teacher类,Teacher类和Student类继承了Person类,造成了菱形继承
菱形继承会造成数据冗余和二义性,
像在Assistant的对象中Person成员会有两份。
//菱形继续
class Person
{
public:
string _name;
};
class Student : public Person
{
protected:
int _num;
};
class Teacher : public Person
{
protected:
int _num;
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse;//主修课程
};
int main()
{
Assistant a;
//菱形继承造成数据二义性
//a._name = "tom"; //error
//需要显示指定访问哪个父类的成员可以解决二义性问题,但是数据冗余问题无法解决
a.Student::_name = "xxx";
a.Teacher::_name = "yyy";
a.Person::_name = "zzz";
}
Assistant对象中_name成员存在两份,造成数据冗余,且访问时需要指定父类
虚继承可以解决数据冗余和二义性的问题
如上面的继承关系,在Student和 Teacher的继承Person时使用虚拟继承,即可解决问题。
//虚继承
class Person
{
public:
string _name;
};
class Student : virtual public Person
{
protected:
int _num;
};
class Teacher :virtual public Person
{
protected:
int _num;
};
class Assistant : public Student, public Teacher
{
protected:
string _majorCourse;//主修课程
};
int main()
{
Assistant a;
//虚继承解决数据冗余和二义性
a._name = "tom";
}
Assistant对象中_name只有一份,虚继承解决了数据冗余和二义性。
注意:
多继承是C++的一个大坑,不建议设计多继承,一定不要设计出菱形继承的关系。
组合:通过组合,一个类可以包含其他类的对象作为成员,从而达到复用功能的目的。
比如:
class Engine
{
public:
void start()
{
// 具体实现
}
};
class Car
{
public:
void drive()
{
_engine.start();
// 具体驾驶实现
}
private:
Engine _engine;
};
Engine类作为Car的一个成员,达到了复用的目的