继承
继承定义
1. C++面向对象有三大特性:封装、继承、多态。
2. 继承机制可以使代码重复使用,允许在保持原有类特性的基础上进行拓展,增加新的成员函数或者成员变量。
继承的格式
1. 下面我们看到Person是基类,也称作父类。Student是派生类,也称作子类。
2. 继承类的格式如下:class 派生类:继承方式 基类。
#include <iostream>
using namespace std;
class Students
{
public:
void identity()
{
//
}
void studying()
{
//
}
protected:
string _name;
string _address;
string _tele;
int _age;
int _id;
};
class Teachers
{
public:
void identity()
{
//
}
void teaching()
{
//
}
private:
string _name;
string _address;
string _tele;
int _age;
int _id;
};
//下⾯我们公共的成员都放到Person类中,Student和teacher都继承Person,就可以复⽤这些成员,就
不需要重复定义了,省去了很多⿇烦。
class Person
{
public:
void identity()
{
//
}
private:
string _name;
string _address;
string _tele;
int _age;
int _id;
};
class Students:public Person
{
public:
void studying()
{
//
}
};
class Teachers:public Person
{
public:
void teaching()
{
//
}
};
继承的方式
1. 由于基类存在3种成员变量,而派生类也存在3种继承方式,所以存在9种不同情况。
2. 基类的private成员变量,无论以哪种方式继承,派生类中都不可见。不可见指的是:逻辑上成员变量发生了继承,但是语法上成员变量在类内、类外都不能直接使用,但是可以通过调用基类的public函数来间接使用。
3. 如果基类成员不想在类外直接使用,又想在派生类中被继承使用,则使用protected限定保护,即protected限定保护是因为继承才出现的。
4. 大小规则:public>protected>private,除了基类的private成员变量,其他成员变量的规则:取两者中更小的那个。
5. 使用关键字class时默认的继承方式是private,使用关键字struct时默认的继承方式是public,不过最好显示的写出继承方式。
6. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
基类和派生类的转换
1. public继承的派生类对象可以赋值给基类的指针和基类的引用,又称之为切片或者切
割 ,寓意 把派生类中基类那部分切出来 , 基类指针 或 基类引用 指向的是 派生类中切出来的基类那部分 。2. 基类对象不能赋值给派生类对象 。
class Person
{
protected :
string _name; // 姓名
string _sex; // 性别
int _age; // 年龄
};
class Student : public Person
{
public :
int _id; // 学号
};
int main()
{
Student sobj;
// 1.派⽣类对象可以赋值给基类的指针或引⽤
Person* pp = &sobj;
Person& rp = sobj;
// 派⽣类对象可以赋值给基类的对象是通过调⽤后⾯会讲解的基类的拷⻉构造完成的
Person pobj = sobj;
//2.基类对象不能赋值给派⽣类对象,这⾥会编译报错
sobj = pobj;
return 0;
}
继承中的作用域
隐藏规则
1. 在继承体系中,基类和派生类都有独立的作用域。
2. 派生类和基类中有同名成员,派生类成员将屏蔽基类对同名成员的直接访问,这种情况叫隐藏。
3. 在 派生类成员函数 中,可以使用 基类::基类成员 显示的访问基类的同名成员 。4. 如果是 成员函数的隐藏 ,只需要 函数名相同 就构成隐藏。5. 所以继承体系里面最好 不要定义同名的成员 。
#include <iostream>
#include <string>
using namespace std;
class Person
{
protected:
int _num=1;
};
class Student:public Person
{
public:
void Print()
{
cout<<_num<<endl;
}
protected:
int _num=11;
};
int main()
{
Student st1;
st1.Print();
return 0;
}
//11
派生类的默认成员函数
1. 派生类的构造函数必须调用基类的构造函数去初始化基类的那一部分成员,需要在派生类构造函数的初始化列表显式调用, 即把基类当成一个整体。此时既需要写基类的构造函数,又需要写派生类的构造函数,还需要在派生类构造函数的初始化列表里面显示的调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的那一部分成员的拷贝,需要在派生类拷贝构造函数的初始化列表显式调用, 即把基类当成一个整体。如果没有资源的开辟消耗,那么可以直接使用默认拷贝构造函数,但是如果存在资源的开辟消耗,那么就需要自己写拷贝构造函数,像构造函数一样。
3. 派生类的赋值重载函数的各种情况和派生类的拷贝构造函数相同,而且派生类的赋值重载函数隐藏了基类的赋值重载函数,所以显示调用基类的赋值运算符时,需要指定基类作用域。
4. 派生类的析构函数的各种情况和派生类的析构函数相同。如果存在资源的开辟消耗,那么就需要自己写析构函数,但是不需要显示调用基类的析构函数了,系统会自动调用。
5. 派生类对象初始化先调用基类构造再调派生类构造。派生类对象析构清理先调用派生类析构再调基类的析构。
//构造函数
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
Person(const char* name)
:_name(name)
{}
protected:
string _name;
};
class Student:public Person
{
public:
Student(int id,const char* address,const char* name)
:_id(id)
,_address(address)
,Person(name)
{}
protected:
int _id;
string _address;
};
int main()
{
Student s1(20,"jiangxi","hsy");
return 0;
}
实现一个不能被继承的类
1. 方法一:基类的构造函数私有化,派生类的构成必须调用基类的构造函数,但是基类的构成函数私有化以后,派生类看不见就不能调用了,那么派生类就无法实例化出对象。
2. 方法二:C++11新增了一个final关键字,派生类就不能继承了。
class Person final
{
public:
Person(const char* name)
:_name(name)
{}
protected:
string _name;
};
继承与友元
1. 友元关系不能继承,也就是说基类友元不能访问派生类私有和保护成员 。
继承与静态成员
1. 基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个派生类,都只有一个static成员实例。
#include <iostream>
#include <string>
using namespace std;
class Person
{
public:
string _name;
static int _count;
};
int Person::_count = 0;
class Student : public Person
{
protected:
int _stuNum;
};
int main()
{
Person p;
Student s;
// 这⾥的运⾏结果可以看到⾮静态成员_name的地址是不⼀样的
// 说明派⽣类继承下来了,⽗派⽣类对象各有⼀份
cout << &p._name << endl;
cout << &s._name << endl;
// 这⾥的运⾏结果可以看到静态成员_count的地址是⼀样的
// 说明派⽣类和基类共⽤同⼀份静态成员
cout << &p._count << endl;
cout << &s._count << endl;
// 公有的情况下,⽗派⽣类指定类域都可以访问静态成员
cout << Person::_count << endl;
cout << Student::_count << endl;
return 0;
}
多继承
1. 单继承:一个派生类只有一个直接基类时称这个继承关系为单继承。
2. 多继承:一个派生类有两个或以上直接基类时称这个继承关系为多继承。
3. 会出现菱形继承,有数据冗余和二义性的问题。