C++57个入门知识点_31 初始化列表及派生类中的函数隐藏(初始化列表:用于调用父类有参构造并初始化、用于自身成员初始化、对于常量成员初始化;函数隐藏:不同作用域里两个数据名称相同,由内向外隐藏)

2023-11-20

上篇C++57个入门知识点_30 父子类,成员类构造析构顺序(存在父子和成员类的构造析构:构造:父类->成员类->自己;析构:自己->成员类->父类;成员类:A类对象作为B类成员,A为B成员类或朋友类)介绍了父类,成员类的构造析构顺序。本篇将介绍与其紧密相关的初始化列表的概念。

总结:

  1. 初始化列表及其作用:用于调用父类的有参构造并做初始化、用于对于自身成员初始化、对于常量成员的初始化
  2. 函数隐藏:函数隐藏同数据隐藏类似:不同作用域里两个数据名称相同,由内向外逐步隐藏
  3. 函数隐藏需要的条件:
    (1)作用域不同
    (2)函数名相同
    (3)优先匹配子类,没有的话匹配父类 ,若要直接调用父类写法:stu.CPerson::test();
    (4)匹配时只看函数的名字,匹配时参数列表、调用约定,返回值均不考虑:子test(int n){}test(){} main(){stu.test(5);}调用首先匹配子类的test函数,由于子类中参数列表是无参的,因此报错。

1. 初始化列表及其作用

用于调用父类的有参构造并做初始化、用于对于自身成员初始化、对于常量成员的初始化

1.1 调用父类的有参构造并做初始化

子类的构造会先调用父类的构造,如果父类的构造是有参的构造,调用父类的时候就得传递参数,此时C++中提供了初始化列表的语法,帮助指定调用父亲的某一个构造

CStudent() :CPerson(123){...}就可以通过子类指定调用父类某一个构造,并传递参数

#include <iostream>

class CPerson
{

public:
	CPerson(int nGender) {
		m_nGender = nGender;
		printf("CPerson(int nGender)\r\n");
	}
	~CPerson() {
		printf("~CPerson()\r\n");
	}

public:
	int m_nPublic;
protected:
	int m_nProtected;
public:
	int m_nGender;
};


class CStudent :public CPerson
{
public:
	//儿子指定调用父亲哪一个有参构造,子类给父类传参数
	CStudent() :CPerson(123)
	{
		printf("CStudent()\r\n");
	}
	~CStudent() {
		printf("~CStudent()\r\n");
	}

private:
	int m_nStuID;
};

int main(int argc, char* argv[])
{
	CStudent stu;
	return 0;
}

当父类存在多个构造函数时,可以通过子类的初始化列表指定父类调用的构造函数。

#include <iostream>

class CPerson
{

public:

	CPerson(int nGender) {
		m_nGender = nGender;
		printf("CPerson(int nGender)\r\n");
	}

	CPerson(int nGender, int n) {
		m_nGender = nGender;
		printf("CPerson(int nGender,int n)\r\n");
	}

	~CPerson() {
		printf("~CPerson()\r\n");
	}

public:
	int m_nPublic;

protected:
	int m_nProtected;

public:
	int m_nGender;

};


class CStudent :public CPerson
{
public:
	//儿子指定调用父亲哪一个有参构造,子类给父类传参数
	CStudent() :CPerson(123, 345)
	{
		printf("CStudent()\r\n");
	}

	~CStudent() {
		printf("~CStudent()\r\n");
	}

private:
	int m_nStuID;
};


int main(int argc, char* argv[])
{
	CStudent stu;
	return 0;
}

运行结果:通过CStudent() :CPerson(123, 345){...}调用了父类CPerson(int nGender, int n) {...}的构造函数,并传递了参数
在这里插入图片描述

1.2 用于对于自身成员初始化

初始化列表不仅可以调用父类指定构造函数,还可以对当前自己类的成员进行初始化

CStudent() :CPerson(123, 345), m_nStuID(4)中, m_nStuID(4)实现了对子类成员的初始化

工程中常用写法为:

CStudent() 
:CPerson(123, 345)
, m_nStuID(4)
{
}
#include <iostream>

class CPerson
{
public:
	CPerson(int nGender) {
		m_nGender = nGender;
		printf("CPerson(int nGender)\r\n");
	}
	CPerson(int nGender, int n) {
		m_nGender = nGender;
		printf("CPerson(int nGender,int n)\r\n");
	}
	~CPerson() {
		printf("~CPerson()\r\n");
	}

public:
	int m_nPublic;
protected:
	int m_nProtected;
public:
	int m_nGender;
};

class CStudent :public CPerson
{
public:
	//儿子指定调用父亲哪一个有参构造,子类给父类传参数
	//也可以对当前类的成员进行初始化
	CStudent() :CPerson(123, 345), m_nStuID(4)
	{
		printf("CStudent()\r\n");
	}

	~CStudent() {
		printf("~CStudent()\r\n");
	}

private:
	int m_nStuID;
};


int main(int argc, char* argv[])
{
	CStudent stu;
	return 0;
}

运行结果:子类父类中的成员均进行了初始化
在这里插入图片描述

1.3 对于const常量成员的初始化

C++57个入门知识点_02_const常量限制(解决C中define无法指明数据类型问题、常量一旦定义生命周期内不可修改、编译器在程序编译时期做的检查、编译时不确定的,才可以通过指针修改运行时的值)中我们知道const常量成员在定义的时候就需要赋值,其初始化有两种写法。

  • 常用的写法
class CStudent :public CPerson
{
public:
	CStudent():CPerson(123,345), m_nStuID(4)
	{
		printf("CStudent()\r\n");
	}

	~CStudent() {
		printf("~CStudent()\r\n");
	}
	
private:
	int m_nStuID;
	//const定义时就需要对其进行赋值,也可以使用初始化列表
	const int n=4;
};
  • 老的编译器中的写法
class CStudent :public CPerson
{
public:
	CStudent() :CPerson(123, 345), m_nStuID(4),n(7)
	{
		printf("CStudent()\r\n");
	}

	~CStudent() {
		printf("~CStudent()\r\n");
	}

private:
	int m_nStuID;
	//const定义时就需要对其进行赋值,也可以使用初始化列表
	const int n;
};

2. 父子类中的函数隐藏

(1)函数隐藏同数据隐藏类似:不同作用域里两个数据名称相同,由内向外逐步隐藏
(2)需要的条件:

  • 作用域不同
  • 函数名相同
  • 优先匹配子类,没有的话匹配父类 ,若要直接调用父类写法:stu.CPerson::test();
  • 匹配时只看函数的名字,匹配时参数列表、调用约定,返回值均不考虑:子test(int n){}test(){} main(){stu.test(5);}调用首先匹配子类的test函数,由于子类中参数列表是无参的,因此报错。

2.1 父子类中的成员变量隐藏

前面我们学习过数据隐藏: 不同的作用里两个变量的名字是相同的,由内向外是逐步隐藏的。参考C++57个入门知识点_09_ 作用域与数据隐藏(作用域的分类、数据隐藏、不同作用域中定义了相同名称的变量,其值不是覆盖,而是存储在不同的地址下,根据作用域(从内->外)查找使用、2.1例子)

  • 父子类中同名成员变量的初始化

在父子类中也存在同样的关系,在上面代码中,父亲有一个int m_nGender;成员,子类中也定义一个int m_nGender;

#include <iostream>

class CPerson
{

public:

	CPerson(int nGender) {
		m_nGender = nGender;
		printf("CPerson(int nGender)\r\n");
	}

	CPerson(int nGender, int n) {
		m_nGender = nGender;
		printf("CPerson(int nGender,int n)\r\n");
	}

	~CPerson() {
		printf("~CPerson()\r\n");
	}

public:
	int m_nPublic;

protected:
	int m_nProtected;

public:
	int m_nGender;
};


class CStudent :public CPerson
{
public:
	CStudent() :CPerson(123, 345), m_nStuID(4), m_nGender(456)
	{
		printf("CStudent()\r\n");
	}

	~CStudent() {
		printf("~CStudent()\r\n");
	}

private:
	int m_nStuID;
	int m_nGender;
	//const定义时就需要对其进行赋值,也可以使用初始化列表
	const int n = 4;
};

int main(int argc, char* argv[])
{
	CStudent stu;
	return 0;
}

运行结果:父子类中m_nGender成员均实现了初始化
在这里插入图片描述

  • 父子类中同名成员变量的隐藏

(1)父类子类均存在的同名成员变量

但是会有一个问题,当在子类的构造中使用如下方式进行赋值,调用的是父类还是子类呢?

得到的结果与数据隐藏的类似,在子类作用域中使用的是子类的成员,父类的成员被隐藏

CStudent() :CPerson(123, 345), m_nStuID(4), m_nGender(456)
	{
		m_nGender = 1;
		printf("CStudent()\r\n");
	}

运行结果:子类中的m_nGender被赋值
在这里插入图片描述
(2)只有在父类中存在的同名成员变量
在这里插入图片描述
(3)如果要指定对父类的进行操作,使用四饼连接符::
在这里插入图片描述

2.2 父子类中的成员函数的隐藏

上面的结果也可以适用于函数,父类子类中均定义void test();

#include <iostream>

class CPerson
{

public:

	CPerson(int nGender) {
		m_nGender = nGender;
		printf("CPerson(int nGender)\r\n");
	}

	CPerson(int nGender, int n) {
		m_nGender = nGender;
		printf("CPerson(int nGender,int n)\r\n");
	}

	~CPerson() {
		printf("~CPerson()\r\n");
	}

	void test() {
		printf("CPerson::test()\r\n");
	}

public:
	int m_nPublic;

protected:
	int m_nProtected;

public:
	int m_nGender;

};

class CStudent :public CPerson
{
public:
	//儿子指定调用父亲哪一个有参构造,子类给父类传参数
	//也可以对当前类的成员进行初始化
	CStudent() :CPerson(123, 345), m_nStuID(4), m_nGender(456)
	{
		m_nGender = 1;
		printf("CStudent()\r\n");
	}

	~CStudent() {
		printf("~CStudent()\r\n");
	}

	void test() {
		printf("CStudent::test()\r\n");
	}

private:
	int m_nStuID;
	int m_nGender;
	//const定义时就需要对其进行赋值,也可以使用初始化列表
	const int n = 4;
};


int main(int argc, char* argv[])
{
	CStudent stu;
	
	//首先匹配子类的,没有的话匹配父类的
	stu.test();
	return 0;
}

结果:stu.test();同样由内向外隐藏,首先匹配子类的,没有的话匹配父类的。

  • 当需要指定调用父类的test();,写法为:stu.CPerson::test();

3.学习视频地址:C++57个入门知识点_31 初始化列表及派生类中的函数隐藏

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++57个入门知识点_31 初始化列表及派生类中的函数隐藏(初始化列表:用于调用父类有参构造并初始化、用于自身成员初始化、对于常量成员初始化;函数隐藏:不同作用域里两个数据名称相同,由内向外隐藏) 的相关文章

随机推荐