C++类与对象四:嵌套类型设计的构造函数实现、this指针和成员方法之间的关系、指向类型成员的指针使用实例

一、嵌套类型设计的构造函数实现

什么是构造函数的初始化列表,它有什么功能呢我们来看这个例子:CData信息是CGoods商品信息的一部分:a part of关系。

//日期类
class CDate
{
public:
	CDate(int y, int m, int d)
	{
		_year = y;
		_month = m;
		_day = d;
	}
	void show()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

//商品类
class CGoods
{
public:
	CGoods(char *n, int a, double p)
	{
		strcpy(_name, n);
		_amount = a;
		_price = p;
	}
	void show()
	{
		cout << "name:" << _name << endl;
		cout << "amount:" << _amount << endl;
		cout << "price:" << _price <<endl;
	}
private:
	char _name[20];
	int _amount;
	double _price;
	CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
};

这里会发生错误:
在这里插入图片描述
       当我们定义了一个对象却没有指定它的构造方式时,它用的就是它的默认构造函数。我们这里并没有指定CDate的构造方式,编译时,因为我们自定义了一个默认构造,此时编译器不会产生默认构造了,编译出错。
       那么年,月,日的信息是如何传递给_date的?如何在CGoods代码中指定构造方式?此时就需要构造函数的初始化列表了。
构造函数的初始化列表:可以对当前对象的成员变量指定初始化方式。成员变量的初始化顺序是以他们定义的顺序初始化的,与构造函数初始化列表中出现的先后顺序无关。

方式1CGoods(char *n, int a, double p, int y, int m, int d)
		:_date(y, m, d)//指定_date对象构造方式,构造函数的初始化列表,先执行       //Cdate _date(y, m, d);
//对当前对象的成员变量指定初始化方式
{
//当前类类型构造函数体,后执行
	strcpy(_name, n);
	_amount = a;//int _amount; _amount = a;
	_price = p;//int _price; _price = p;
}

方式2CGoods(char *n, int a, double p, int y, int m, int d)
		:_date(y, m, d)
		,_amount(a)//int _amount = a;
		,_price(p)//double _price = p;
		//对当前对象的成员变量指定初始化方式
	{
		//当前类类型构造函数体,后执行
		strcpy(_name, n);
	}

我们修改后测试一下:测试成功。
在这里插入图片描述
我们再来看一个实例:增强对它的理解。
思考一下,ma,mb分别打印的结果是什么?

class Test
{
public:
	Test(int data = 10):mb(data), ma(mb){}
	void show()
	{
		cout << "ma:" << ma << " mb:" << mb << endl;
	}
private:
	int ma;
	int mb;
};

int main()
{
	Test t;
	t.show();

	return 0;
}

       打印结果分析: 当我们生成一个对象的时候,对象的成员变量初始化方式若没有在构造函数初始化列表中指定,则使用默认构造,即没有初始化。若在构造函数的初始化列表中初始化了,那么成员变量的初始化顺序是以他们定义的顺序初始化的。因此ma先初始化,此时mb还没有值,即0xcccccccc;mb再初始化即10。
在这里插入图片描述

二、this指针和成员方法之间的关系

2.1、普通成员方法与静态成员方法

普通的成员方法特点:编译器会添加一个this形参变量
1.属于类的作用域。
2.调用时使用对象调用(常对象无法调用)。
3.可以任意访问对象的私有成员。(protected暂时不考虑)。

静态成员方法特点:不会生成this形参
1.属于类的作用域。
2.调用时使用类名作用域调用。
3.若一个方法访问的是所有对象共享的信息,我们最好使用静态方法。
4.可以任意访问对象的私有成员,仅限于不依赖对象的成员(仅能调用其它的static成员)。

案例1: 我们先来看一个例子,继续看上面那个商品类,此时我们若定义了多个商品,想记录商品的总数量,于是我们这样定义可以吗?

private:
	char _name[20];
	int _amount;
	double _price;
	CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
	int _count;//用来记录商品对象的总数量
};

答案是否定的,因为我们把_count定义成为了一个普通的成员变量,当我们用CGoods定义对象的时候,每一个对象都有自己的一个_count,每一个_count只能描述一个商品对象的信息,不能描述所有商品的信息。因此我们使用静态成员变量,它是所有对象所共享的,类内只是声明,类外实现定义。静态成员变量不属于对象,是属于类级别的。 当我们再次计算对象大小的时候,静态成员变量不纳入对象内存的。每次定义一个新的对象会调用构造函数,对_count进行++,即可统计。

类内:
private:
	char _name[20];
	int _amount;
	double _price;
	CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
	static int _count;//静态成员变量,类内只是声明,需要在类外定义,用来记录商品对象的总数量
类外:
//静态成员变量一定要在类外进行定义并且初始化
int CGoods::_count = 0;//.bss段

因此实现代码为:

//日期类
class CDate
{
public:
	CDate(int y, int m, int d)
	{
		_year = y;
		_month = m;
		_day = d;
	}
	void show()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

//商品类
class CGoods
{
public:
	CGoods(const char *n, int a, double p, int y, int m, int d)
		:_date(y, m, d)//指定_date对象构造方式,构造函数的初始化列表,先执行
		,_amount(a)
		,_price(p)
		//对当前对象的成员变量指定初始化方式
	{
		//当前类类型构造函数体,后执行
		strcpy(_name, n);
		_count++;//记录所有产生的新对象的数量
	}
	void show()//打印商品私有信息
	{
		cout << "name:" << _name << endl;
		cout << "amount:" << _amount << endl;
		cout << "price:" << _price <<endl;
		_date.show();
		cout << endl;
	}
	void showCGoodsConut()//打印商品所有共享信息
	{
		cout << "所有商品的种类数量是:" << _count << endl;
	}
private:
	char _name[20];
	int _amount;
	double _price;
	CDate _date;//CDate类定义的一个对象作为CGoods类的成员变量,即成员对象
	static int _count;//静态成员变量,类内只是声明,需要在类外定义,用来记录商品对象的总数量
};
//静态成员变量一定要在类外进行定义并且初始化
int CGoods::_count = 0;

int main()
{
	CGoods good1("商品1", 100, 35.0, 2019, 5, 12);
	good1.show();

	CGoods good2("商品2", 100, 35.0, 2019, 5, 12);
	good2.show();

	CGoods good3("商品3", 100, 35.0, 2019, 5, 12);
	good3.show();

	CGoods good4("商品4", 100, 35.0, 2019, 5, 12);
	good4.show();

	//统计所有商品的总数量
	good4.showCGoodsConut();
	return 0;
}

测试结果:成功统计
在这里插入图片描述
虽然上面代码完成了我们的功能,打印的是所有商品的信息。但是为什么要使用good4调用呢,这不符合我们的逻辑。那我们只用类名调用可以吗?

CGoods::showCGoodsConut();

当然不行,它只是一个普通的成员方法,编译器编译时会产生this指针,调用时必须要传入调用对象的地址,但此时没有对象。因此我们想访问所有对象共享的信息,不能将它写为普通方法用对象去调用它,应该使用类名去调用它,我们需将其写为静态成员方法。

//静态成员方法
static void showCGoodsConut()//打印商品所有共享信息
{
	cout << "所有商品的种类数量是:" << _count << endl;
}

静态成员方法使用类的作用域调用:不依赖于this指针,不需要接收对象的地址。

CGoods::showCGoodsConut();
2.2、常成员方法

常成员方法: 如果一个方法是只实现读操作而不实现写操作的成员方法,一律实现成const常成员方法。普通对象可以调用,常对象也能调用。
常成员方法特点:
1.常成员方法产生const 类类型 * this。
2.属于类的作用域
3.调用依赖于一个对象,普通对象或者常对象都可以。
4.可以任意访问对象的私有成员,只能读不能写。

案例2: 常对象调用普通方法:常对象无法调用普通成员方法
此时若有一个非卖品对象:good5

const CGoods good5("非卖品商品5", 100, 35.0, 2019, 5, 12);
good5.show();

我们打印一下信息:编译器报错,
在这里插入图片描述
分析一下: good5为const CGoods* 类型,而普通方法show生成的是CGoods *this类型,因此会报错。若想接收,形参必须也是const CGoods *this类型。因此,修改如下:将其写为常方法,普通对象能调用,常对象也可以调用。

//常成员方法
//CDate中的show()方法
void show()const
{
	cout << _year << "/" << _month << "/" << _day << endl;
}

//CGoods中的show()方法
void show()const//const CGoods *this
{
	cout << "name:" << _name << endl;
	cout << "amount:" << _amount << endl;
	cout << "price:" << _price <<endl;
	_date.show();
	cout << endl;
}

案例3:常对象可以任意访问对象的私有成员,只能读不能写。
我们尝试对_price进行修改

//常成员方法
void show()const//const CGoods *this
{
	cout << "name:" << _name << endl;
	cout << "amount:" << _amount << endl;
	cout << "price:" << _price <<endl;
	_price = 20;
	_date.show();
	cout << endl;
}

编译器报错:
在这里插入图片描述

三、指向类型成员的指针使用实例

3.1普通成员变量使用实例

普通成员变量:我们定义指针指向类的成员变量,前面需要添加类的作用域。而且必须依赖于对象。
实例1:调用普通成员变量

class Test
{
public:
	void func()//普通成员方法
	{
		cout << "call Test::func" << endl;
	}
	static void static_func()//静态成员方法
	{
		cout << "Test::static_func" << endl;
	}
	int ma;//普通成员变量
};

int main()
{
	int *p = &Test::ma;//普通指针指向整型成员变量
	*p = 20;

	return 0;
}

执行结果:编译器报错
在这里插入图片描述
&Test::ma类型编译器认为是加了类的作用域,因此我们定义指针时,也需要加上类的作用域,是类作用域里的整型变量。当访问ma时必须给前面加上对象。
①我们在栈上定义一个对象:

int main()
{
	Test t1;//栈上
	
	int Test::*p = &Test::ma;//普通指针指向整型成员变量
	t1.*p = 20;
	cout << t1.*p << endl;
	
	return 0;
}

修改成功:
在这里插入图片描述
②我们在堆上定义一个对象:

int main()
{
	Test *t2 = new Test();//堆上

	int Test::*p = &Test::ma;//普通指针指向整型成员变量
	t2->*p = 30;
	cout << t2->*p << endl;
	delete t2;
	
	return 0;
}

修改成功:
在这里插入图片描述
因此,我们定义指针指向类的成员变量,前面需要添加类的作用域。

3.2静态成员变量使用实例

静态成员变量:不依赖对象。
实例2:调用静态成员变量

class Test
{
public:
	void func()//普通成员方法
	{
		cout << "call Test::func" << endl;
	}
	static void static_func()//静态成员方法
	{
		cout << "Test::static_func" << endl;
	}
	int ma;//普通成员变量
	static int mb;//静态成员变量
};

int Test::mb;

int main()
{
	int *p1 = &Test::mb;
	*p1 = 40;
	cout << *p1 << endl;

    return 0;
}

修改成功:
在这里插入图片描述

3.3指向普通成员方法的指针使用实例

实例3:调用普通成员方法

//指向成员方法的指针
void (*pfunc)() = &Test::func();
(*pfunc)();

编译出错:
在这里插入图片描述
因此我们必须加上类的作用域与对象来调用:

Test t1;//栈上
Test *t2 = new Test();//堆上

//指向成员方法的指针
void (Test::*pfunc)() = &Test::func;
(t1.*pfunc)();
(t2->*pfunc)();

调用成功。
在这里插入图片描述

3.4指向成员方法的指针使用实例

实例4:调用静态成员方法

Test::static_func();

调用成功。
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值