目录
一、什么是继承
定义:
继承(inheritance)机制是面向对象程序设计中使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能。这样产生的新类,称派生类(或子类),被继承的类称基类(或父类)。
继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程。之前接触的复用都是函数复用,继承是类设计层次的复用。
二、继承的格式
class 新类的名字:继承方式 继承类的名字{};
新类就是继承的类,称为子类或派生类,被继承的类被称为父类或基类。
继承的权限:
继承的总结:
1.基类private成员无论以什么方式继承到派生类中都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。
2.基类private成员在派生类中不能被访问,如果基类成员不想在派生类外直接被访问,但需要在派生类中访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。
3.基类的私有成员在子类都是不可见;基类的其他成员在子类的访问方式就是访问限定符和继承方式中权限更小的那个(权限排序:public>protected>private)。
4.使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,但最好显式地写出继承方式。
三、子类和父类
一、子类对父类的赋值
子类可以直接对父类进行赋值,因为父类有的子类都有,赋值的也只是父类的那一部分。
子类对父类的赋值有三种方式
直接赋值:
student st;//子类
human hm;//父类
hm = st;
引用赋值:
student st;//子类
human& hm=st;父类
指针赋值:
student st;//子类
human* hm=&st;//父类
二、子类与父类的同名成员变量
如果在子类与父类中出现了同名的成员变量,父类的成员变量会被隐藏,默认使用子类的成员变量,如果想要使用父类的成员变量,需要用父类名直接修饰限定。
void print()
{
cout << human::name << endl;
}
三、子类和父类的同名成员函数
class human {
public:
string name = "小明";
void print()
{
cout << name << endl;
}
};
class student :public human {
public:
string name = "小红";
void print()
{
cout << name << endl;
}
};
int main()
{
student st;
st.print();
return 0;
}
可以看到父类和子类有同名的成员函数,这时候去运行,调用的只会是子类的成员函数。
如果想要调用父类的成员函数也要和成员变量一样,加上修饰符限定。
四、子类的默认成员函数
一、构造函数
编译器会默认先调用父类的构造函数,再调用子类的构造函数
class human {
public:
human(string name = "小明")//先调用:父类默认构造调用一个print打印name
:_name(name)
{
cout << name << endl;
}
protected:
string _name;
};
class student :public human {//后调用:子类默认构造调用一个print打印name和age
public:
student(string name,int age)
:_age(age)
{
cout << name << endl<<age<<endl;
}
protected:
int _age;
};
int main()
{
student st("小红", 18);
return 0;
}
子类的构造函数会在初始化列表开始时调用父类的默认构造,也可以自己显示调用父类的构造函数。
二、析构函数
class human {
public:
human(string name = "小明")
:_name(name)
{}
~human()
{
cout << "我是父类" << endl;
}
protected:
string _name;
};
class student :public human {
public:
student(string name,int a = 20)
:age(a)
{}
~student()
{
cout <<"我是子类"<< endl;
}
protected:
int age;
};
int main()
{
student st("小明", 18);
return 0;
}
子类的析构函数会自动执行父类的析构函数,不管有没有显示调用,因为编译器奉行,先析构子类,再析构父类,以防止一些不必要的bug。
三、拷贝构造
class human {
public:
human(string name="小明")
:_name(name)
{
cout << name << endl;
}
protected:
string _name;
};
class student:public human {
public:
student(string name, int age)
:_age(age)
{
cout << name << endl << age << endl;
}
student(student& s)
:human(s)//直接将st传过来通过切片拿到父类中的值
,_age(s._age)//拿除了父类之外的值
{
cout << s._age << endl<<s._name<<endl;
}
protected:
int _age;
};
int main()
{
student st("小红",18);
student st2(st);
return 0;
}
子类中调用父类的拷贝构造时,直接传入子类对象即可,父类的拷贝构造会通过“切片”拿到父类的那一部分。
四、赋值运算符重载
class human {
public:
human(string name = "小明")
:_name(name)
{
}
human& operator=(const human& p)
{
if (this != &p)
{
cout << "调用父类" << endl;
_name = p._name;
}
return *this;
}
protected:
string _name;
};
class student :public human {
public:
student(string name, int age)
:_age(age)
{
}
student(student& s)
:human(s)
, _age(s._age)
{
}
student& operator=(const student& s)
{
if (this != &s)
{
cout << "调用了子类" << endl;
human::operator=(s);//必须调用父类运算符
_age = s._age;
_name = s._name;
}
return *this;
}
protected:
int _age;
};
int main()
{
student st("小红", 18);
student st2(st);
student st3("小刚", 16);
st = st3;
return 0;
}
子类的operator=必须要显式调用父类的operator=完成父类的赋值。
因为子类和父类的运算符,编译器默认给与了同一个名字,所以构成了隐藏,所以每次调用=这个赋值运算符都会一直调用子类,会造成循环,所以这里的赋值要直接修饰限定父类。
标签:name,继承,子类,成员,c++,human,父类 From: https://blog.csdn.net/2401_82609762/article/details/140557947