1.继承的定义及说明:
class person
{
public:
void print()
{
cout<<_name<<endl;
cout<<_age<<endl;
}
protected:
string _name="lwt";
int _age=23;
};
class student : public person //student称为子类或者派生类,public称为继承方式,person称为父类或者基类
{ //这就是继承的定义格式, student继承了person类
public:
int stuid;
}
子类继承父类后,父类的成员会变为子类的一部分,体现了子类复用了父类成员;
访问限定符:public , protected, private;
继承方式: public继承,protected继承,private继承;
一般用到的继承为public继承,基类成员在基类中为什么访问方式,被继承到子类中访问方式不变;但是基类中的private成员 ,不管以什么方式继承,在子类中都是不可见的;
2.基类和派生类对象赋值转换
#1.派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切 割。寓意把派生类中父类那部分切来赋值过去。
2. 基类对象不能赋值给派生类对象
3. 基类的指针可以通过强制类型转换赋值给派生类的指针。但是必须是基类的指针是指向派生类对象时才 是安全的
class person
{
protected:
string _name;
string _sex;
int _age;
}
class student : public person
{
public:
int _number;
}
int main()
{
student a;
person b=a; //是可以的 允许派生类对象给基类对象赋值
a=b; //是不行的 不允许基类对象给派生类对象赋值
person* p=&a;
person* pp=&b;
person& c=a;
student* p1=(student*)p;// 是可以的
student* p2=(student*)pp; //这种情况可能会出现越界访问的问题
return 0;
}
派生类中默认成员函数:
#1. 派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函 数,则必须在派生类构造函数的初始化列表阶段显示调用。
2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类 对象先 清理派生类成员再清理基类成员的顺序。
5. 派生类对象初始化先调用基类构造再调派生类构造。
6. 派生类对象析构清理先调用派生类析构再调基类的析构。
class person
{
person(const char* name="hello") //全缺省构造函数
:_name(name)
{
cout<<"person()"<<endl;
}
person(const person& p) //拷贝构造
:_name(p._name)
{
cout<<"person(const person& p)"<<endl;
}
person& operator=(const person& p) //赋值运算符重载
{
if(this!=&p)
{
_name=p._name;
}
return *this;
}
~person()
{
cout<<"~person()"<<endl;
}
protected:
string _name;
};
class student : public person
{
public:
student(const char* name,int num) //子类构造函数
:person(name) //显示调用父类的构造函数 进行父类成员的初始化,如果不显示调用,则调用父类的
,_num(num) //默认构造函数
{
cout<<"student()"<<endl;
}
student(const student& s) //子类拷贝构造
:person(s) //显示的调用父类拷贝构造
,_num(s._num)
{
cout<<"studen(const student& s)"<<endl;
}
student& operator=(const student& s)
{
cout<<"Student& operator= (const Student& s)"<< endl;
if (this != &s)
{
Person::operator =(s); //显示调用父类的赋值运算符重载函数 而且要用类名+作用域运算符
_num = s ._num; //否则会发生无限递归
}
return *this;
}
~student() //不允许显示调用父类的析构函数,因为在底层编译器对父类和子类的析构函数
{ //名字都做了修改 两个都修改为 ~destructor;
cout<<"~student()"<<endl;
person::~person(); //这样显示调用不对
}
protected:
int _num;
}
int main()
{
Student s1 ("jack", 18);
Student s2 (s1);
Student s3=s1; //拷贝构造
s1 = s3 ; //赋值运算符重载
}