【从 C 向 C++ 进阶】- 类 - 16. 类类型转换

从 C 向 C++ 进阶系列导航


1. 类类型隐式转换陷阱

C++ 的类类型的转换规则如下:

  • 转换函数定义在源对象(转换对象)类中,是转换源对象的成员函数。
  • 一旦为转换源类型提供了到目标类型的转化函数,就可以将源类对象以隐式转化的方式转换得到目标类型对象。
  • 当没有定义转换函数时,编译器会尽力把源对象转化为目标对象,且容易触发隐私转换,不一定报错。
  • 实验:
class Test
{
private:
	int m_var;

public:
	Test(int num)
	{
		m_var = num;
	}
	int GetVar()
	{
		return m_var;
	}
};

int main(int argc, char *argv[])
{
	Test obj_A = 0;
	obj_A = 2;
	cout << "obj_A.GetVar() = " << obj_A.GetVar() << endl;	// obj_A.GetVar() = 2
	obj_A = 3.14;
	cout << "obj_A.GetVar() = " << obj_A.GetVar() << endl;	// obj_A.GetVar() = 3
}

以上在将整形字面量 2 和浮点型字符量 3.14 分别赋值给类对象 obj_A 时,编译器会尽力地进行类型转,例如:

obj_A = 2; => obj_A = Test(2);
obj_A = 3.14; => obj_A = Test(3.14);

以上的构造函数又称为转换构造函数。当然,这样的隐式类类型转换是不安全的,因为程序编译并没有报错,但程序的结果却未必是所期望的。


2. explicit 关键字

explicit 关键字用来声明构造函数,表示编译器不能自动进行隐式类型转换,需要在程序中进行显式地声明类型转换。

  • 实验:
class Test
{
private:
	int m_var;

public:
	explicit Test(int num)
	{
		m_var = num;
	}
	int GetVar()
	{
		return m_var;
	}
};

int main(int argc, char *argv[])
{
	// Test obj_A = 1;		// error: conversion from ‘int’ to non-scalar type ‘Test’ requested
	Test obj_A(1);
	Test obj_B = static_cast<Test>(1);
	cout << "obj_B.GetVar() = " << obj_B.GetVar() << endl;	// obj_B.GetVar() = 1
	obj_B = static_cast<Test>(2);
	cout << "obj_B.GetVar() = " << obj_B.GetVar() << endl;	// obj_B.GetVar() = 2
}

需要注意的是,在使用 explicit 关键字声明后,在类的初始化时应使用括号形式进行赋值,否则必须显式声明转换类型。


3. operator 关键字

explicit 关键字用来声明成员函数,使其作为类型转换函数。类型转换函数可以将类对象转换为其它类型,函数形态为:

operator Type()
{
    ...
    return obj;
}

其中,Type 为需要转换的目标类型,obj 为转换的目标类型对象。特殊地,类型转换函数是没有参数与返回类型的,因为参数与返回类型都被隐藏在函数体内。

  • 实验:
class Test_A
{
private:
	int m_var;

public:
	Test_A(int num)
	{
		m_var = num;
	}
	int GetVar()
	{
		return m_var;
	}
	friend class Test_B;
};

class Test_B
{
private:
	int m_var;

public:
	Test_B(int num)
	{
		m_var = num;
	}
	int GetVar()
	{
		return m_var;
	}
	operator Test_A()
	{
		Test_A obj = 0;
		obj.m_var = m_var;
		return obj;
	}
};

int main(int argc, char *argv[])
{	
	Test_A obj_A = 1;
	cout << "obj_A.GetVar() = " << obj_A.GetVar() << endl;	// obj_A.GetVar() = 1
	Test_B obj_B = 2;
	cout << "obj_B.GetVar() = " << obj_B.GetVar() << endl;	// obj_B.GetVar() = 2
	Test_A obj_C = obj_B;
	cout << "obj_C.GetVar() = " << obj_C.GetVar() << endl;	// obj_C.GetVar() = 2
}


4. 类类型转换的冲突

类型转换函数可能会与转换构造函数相冲突。

  • 示例:
class Test_B;

class Test_A
{
private:
	int m_var;

public:
	Test_A(int num)
	{
		m_var = num;
	}
	Test_A(Test_B& obj);
	int GetVar()
	{
		return m_var;
	}
	friend class Test_B;
};

class Test_B
{
private:
	int m_var;
		
public:
	Test_B(int num = 0)
	{
		m_var = num;
	}
	int GetVar()
	{
		return m_var;
	}
	operator Test_A()
	{
		Test_A obj = 0;
		obj.m_var = m_var;
		return obj;
	}
};

Test_A::Test_A(Test_B& obj)
{
	m_var = obj.GetVar();
}

int main(int argc, char *argv[])
{	
	Test_B obj_B(1);
	// Test_A obj_A = obj_B;	// error : conversion from ‘Test_B’ to ‘Test_A’ is ambiguous class Test_B’
	cout << "obj_A.GetVar() = " << obj_A.GetVar() << endl;
}

编译器不知道到底该调用转换构造函数还是类型转换函数,而解决方法就是在声明 Test_A(Test_B& obj); 前加上 explicit 关键字防止转换构造函数发生隐式转换。

在实际工程中,往往使用形为 Type toType() 的函数来代替类型转换函数。例如转换为整形的函数形为:

int toInt()
{
    ...
}
发布了60 篇原创文章 · 获赞 36 · 访问量 5945

猜你喜欢

转载自blog.csdn.net/qq_35692077/article/details/97118425
今日推荐