14.16 类型转换构造函数、运算符与类成员指针

本文详细介绍了C++中的类型转换构造函数,如何将其他数据类型转换为特定类对象,以及类型转换运算符的功能,包括隐式和显式转换,类型转换运算符的定义和使用范例。还讨论了类型转换可能带来的二义性和最佳实践,以及类成员函数指针和变量的应用。
摘要由CSDN通过智能技术生成

一:类型转换构造函数

比如,他们能够把一个数字转换成一个类对象
构造函数种类:常规不带参数的(默认构造函数),带一个参数,带两个参数…
拷贝构造函数,移动构造函数
特点回顾
<1>以类名作为函数名
<2>没有返回值
有一种构造函数 叫类型转换构造函数,主要能力是:它可以将某个其他的数据类型转换成该类类型的对象

类型转换构造函数的特点
<1>只有一个参数,该参数又不是本类的const引用。(const &A)该参数其实就是待转换的数据类型,所以显然待转换的数据类型都不应该是本类类型。
<2>在类型转换构造函数中,我们要指定转换的方法(在这个函数中要干什么);

class TestInt  //保存0-100之间的一个数字
{
public:
	// explicit:禁止做隐式类型转换
	TestInt(int x = 0) :m_i(x)  // 类型转换构造函数[也是一个只带一个参数的普通构造函数],本构造函数就可以将一个int数字转换成类类型TestInt
	{
		if (m_i < 0) m_i = 0;
		if (m_i > 100) m_i = 100;
	}
public:
	int m_i;
};

TestInt ti = 12;  //隐式类型转换。将数字12转换成TestInt对象(调用了类型转换构造函数)
//编译器用12这个数字通过调用TestInt类的类型构造函数来创建一个临时的TestInt对象,并把这个对象构造到ti这个预留空间里面去了

TestInt ti = TestInt(12);  //这也是调用类型转换构造函数

TestInt ti2(22);  //调用了类型转换构造函数,但这里并没有进行隐式类型转换
//编译器用12这个数字通过调用TestInt类的类型构造函数来创建一个临时的TestInt对象,并把这个对象构造到了ti的预留空间里面去了。

二:类型转换运算符(类型转换函数)

能力和类型转换构造函数能力相反。是特殊的成员函数,它能够将一个类类型对象转换成某个其他数据类型。
格式:
operator type() const;
<1>const 是可选项,const 表示一般不应该改变待转换对象的内容,但不是必须有 const。
<2>type:表示转换成的某种类型。只要是能够作为函数返回的类型,都可以。数组指针,函数指针,引用等等类型都可以。
<3>类中转换运算符,没有形参(形参列表为空),因为类型转换运算符都是隐式执行的,所以根本也没有办法给它传递参数。同时也不能指定返回类型,但是它却能返回一个 type 指定的类型的值的。
<4>必须定义为类的成员函数。

class TestInt
{
public:
	TestInt(int x = 0) :m_i(x)
	{
	}
	// 类型转换运算符,可以把本类类型对象转换成其他类型
	operator int() const
	//explicit operator int() const  //explicit:禁止编译器为我们做隐式类型转换
	{
		return m_i;
	}
public:
	int m_i;
};

TestInt ti = 12;
TestInt ti2;
ti2 = 6;  //编译器用6生成临时的TestInt对象(调用TestInt类型转换构造函数),又调用赋值运算符把临时对象的内容给了ti2。
cout << typeid(ti2).name() << endl;  //class TestInt

int k = static_cast<int>(ti2) + 5;  //调用operator int() const
int k2 = ti2 + 5; //11:调用operator int() const将ti2转换成了int,再和5做加法运算,结果给k。
	   		      //ti2转int是隐式类型转换。
int k3 = ti2.operator int() + 5;  //显式调用,没有形参,所以()内为空

1>显示类型转换运算符 explicit
2>有趣范例:类对象转换成函数指针

class TestInt
{
public:
	TestInt(int i) : m_i(i) {};
	// 定义一个函数指针类型,代表的函数带一个int形参,没有返回类型
	typedef void(*tfpoint)(int);
	//using tfpoint = void(*)(int);

public:
	static void mysfunc(int v)  //静态成员函数
	{
		int test = 1;
	}

	// 新的类型转换运算符,能把本类类型对象转换成一个函数指针类型
	operator tfpoint()  //const不是必须加的,所以这里不加
	{
		// 必须要返回一个函数指针
		return mysfunc;  //函数地址(函数名)作为函数指针类型返回即可
	}

public:
	int m_i;
};

int main()
{
	TestInt myi(12);
	myi(123);  //看起来是个可调用对象的感觉,相当于调用了两个函数:1)类型转换运算符[转换成函数指针类型;2)通过函数指针调用具体的函数。
	(myi.operator TestInt::tfpoint())(123);
	return 0;
}

三:类型转换二义性问题

二义性:这么干也行,那么干也行,导致编译器不知道该怎么干,所以编译器只能报错。
TestInt aa;
int abc = aa + 12; //二义性
建议:在一个类中,尽量只出现一个类型转换运算符。

class CT1
{
public:
	CT1(int ct) {}  //类型转换构造函数
};

class CT2
{
public:
	CT2(int ct) {}  //类型转换构造函数
};

void testfunc(const CT1& C) {}
void testfunc(const CT2& C) {}

int main()
{
	testfunc(100);  //二义性,因为100是个int类型,可以转成成CT1对象,也可以转换成CT2类型对象
	testfunc(CT1(101));  //明确调用void testfunc(const CT1& C) {},这种手段表明代码设计的不好
	return 0;
}

四:类成员函数指针

是个指针,指向类成员函数

class CT
{
public:
	void ptfunc(int temp)
	{
		cout << "ptfunc普通成员函数 value = " << temp << endl;
	}

	//类中有虚函数则编译器会给该类生成虚函数表
	virtual void virtualfunc(int temp)
	{
		cout << "virtualfunc虚成员函数被调用 value= " << temp << endl;
	}

	static void staticfunc(int temp)
	{
		cout << "staticfunc静态成员函数被调用 value = " << temp << endl;
	}
};

<1>对于普通成员函数的函数指针
格式:类名::*函数指针变量名 来声明普通成员函数指针
&类名::成员函数名 来获取类成员函数地址,这个是真正的内存地址

大家注意:成员函数是属于类的,不属于类对象,只要有类在就有成员函数地址在。
但是你若要使用这个成员函数指针,就必须把它绑定到一个类对象上才能调用。
使用函数指针的格式:类对象名.*函数指针变量名来调用,如果是个对象指针,则调用格式"指针名->函数指针变量名"来调用

//定义一个普通的类成员函数指针
void (CT:: * mypointpt)(int);  //一个类成员函数指针变量的定义,变量名字为mypointpt
mypointpt = &CT::ptfunc;  //类成员函数指针变量被赋值

CT ct, * pct;
pct = &ct;
(ct.*mypointpt)(100);  //对象ct,调用指针变量mypointpt所指向的成员函数:ptfunc
(pct->*mypointpt)(200);  //对pct所指的对象,调用指针变量mypointpt所指向的成员函数:ptfunc
//() 是函数调用运算符,优先级是高于.*和->,所以要用括号提高优先级

<2>对于虚成员函数的函数指针
也必须要绑定到一个类对象才能调用

void(CT:: * mypointvirtual)(int) = &CT::virtualfunc;  //这是个真正的内存地址

(ct.*mypointvirtual)(100);
(pct->*mypointvirtual)(200);

<3>对于静态成员函数的函数指针
使用"*函数指针变量名"来声明静态成员函数指针,使用"&类名::成员函数名"来获取类成员地址,这个也是真正的地址。

//定义一个静态的类成员函数指针并赋值
void(*mypointstatic)(int) = &CT::staticfunc;

mypointstatic(100);  //直接使用静态成员函数指针名即可调用

五:类成员变量指针

<1>对于普通成员变量

class CT
{
public:
	virtual ~CT()
	{
	}
public:
	int m_a;  //普通成员变量,属于对象

};

int main()
{
	int CT::* mp = &CT::m_a;  //定义一个类成员变量指针(有偏移量),大家注意这种写法
	//0x00000004{ ? ? ? } 并不是真正意义上的指针
	//它不是指向某个内存中的某个地址,而是该成员变量,与该类对象指针之间的偏移量
	CT ctestmp;  //当生成类对象时,如果这个类中有虚函数表,则对象中,就会有一个指向这个虚函数表的指针;这个指针占有4个字节。
	ctestmp.*mp = 189;  // 通过类成员变量指针来修改成员变量值,等价于ctestmp.m_a = 189;

	cout << ctestmp.*mp << endl;  //189
	cout << ctestmp.m_a << endl;  //189
	return 0;
}

<2>对于静态成员变量
这种指向静态成员变量的指针,是有真正的内存地址。

class CT
{
public:
	virtual ~CT()
	{
	}
public:
	static int m_static;  //声明:静态成员变量,属于类,不属于对象。
};

int CT::m_static = 1;  //静态成员变量的定义

int main()
{
	int* stcp = &CT::m_static;  //定义一个静态成员变量指针
	*stcp = 796;  //等价于CT::m_static = 796
	cout << *stcp << endl;
	return 0;
}

六:总结

C++对象模型(高级的知识)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值