本主题讨论C++如何处理用户自定义类型的转换。在讨论前,我们先复习一下C++如何处理内置类型转换的。
1.如果这两种类型兼容,则自动将值转换为接收变量的类型。例如
cpp
long a = 'a'; //char 转为 long
double b = 11;//int 转为 double
int c = 3.33; //double 转为 int,有警告但可以转
2.类型不兼容,不能自动转换,但可以强制转换。例如
cpp
int *p = 10; //错误,类型错误
int *p = (int *)p;//可以.强制类型转换,这种赋值会出现野指针,但合法
1.转换构造函数
利用构造函数将基本类型或其它类构造当前类对象。
下面定义一个Complex复数类。
cpp
//complex.h
#pragma once
//复数类,演示类型转换
#include <iostream>
using namespace std;
class Complex
{
private:
int real;//实部
int imag;//虚部
public:
Complex() { real = imag = 0; } //默认构造函数.内联函数
Complex(int r) { real = r; imag = 0; }//带实部的构造函数,转换构造函数
Complex(int r, int m) { real = r; imag = m; }//实部,虚部构造函数,内联函数
Complex operator +(const Complex& com) const;//重载两个复数相加
Complex operator -(const Complex & com)const;//重载两个复数相减
friend ostream& operator<<(ostream& os, const Complex& com);//输出,友元函数
};
cpp
//complex.cpp
#include "complex.h"
Complex Complex::operator +(const Complex& com) const//重载两个复数相加
{
return Complex{ real + com.real, imag + com.imag };//最推荐
// return Complex(real + com.real, imag + com.imag); //推荐
//return { real + com.real, imag + com.imag };//不太推荐
// return (real + com.real, imag + com.imag);//可能错误,禁止使用
}
Complex Complex::operator -(const Complex& com)const//重载两个复数相减
{
return Complex(real-com.real,imag-com.imag);
}
ostream& operator<<(ostream& os, const Complex& com)//输出,友元函数
{
os << com.real << "+" << com.imag << "i" << endl;
return os;
}
测试代码如下:
cpp
//test.cpp
#include "complex.h
int main()
{
Complex c1 = {1,2};
Complex c2 = {5,6};
Complex c3 = 5;//自动类型转换,把int转为Complex类
cout << c1 << c2<< c3<< endl;
return 0;
}
其中c1,c2都是定义的正常的类型,但c3可以被一个整数赋值。
在C++中,接受一个参数的构造函数,可以将这种参数的类型转为该类类型,这种构造函数称为转换构造函数。如上把整型转为Complex类。
转换构造函数的作用是将一个其他类型转换成一个类类型。
cpp
Complex(int r) { real = r; imag = 0; }//接受一个参数的构造函数
也就是说,可以编写这样的代码:
cpp
Complex c3 = 5;//自动类型转换,把int转为Complex类
Complex c4 = Complex(5);//这个和上面的等价,把int转为Complex类
程序使用构造函数来创建一个类对象,这一过程称为隐式转换。
注意:只有接受一个参数的构造函数才能作为转换构造函数(如果有多个参数,但其它参数都有默认值也可以)。
cpp
//下面的构造函数不能同时存在,会出现重定义(和上面的Complex(int r)也会重定义)
Complex(int r, int m=0) { real = r; imag = m; }//可以把int转为Complex类型
Complex(int r=0, int m=0) { real = r; imag = m; }//可以把int转为Complex类型
将构造函数用于自动类型转换似乎是不错的特性,但是,当你拥有更丰富的C++经验时,将发现这种自动转换有时并不是你想要的,而且会导致意外转换。
C++新增关键字explicit(明确的),用于关闭自动转换。也就是说,可以这样声明构造函数:
cpp
explicit Complex(int r) { real = r; imag = 0; }//接受一个参数的构造函数,且不允许隐式转换
这样就不能自动转换,但可以显示转换,即:
cpp
//Complex c3 = 5;//错误,不能自动(隐式)转换
Complex c3 = (Complex)5;//可以,显示转换
Complex c4 = Complex(5);//可以,显示转换
这样能更好的保证程序的可读性,同时不影响功能。
2.类型转换函数
上面的函数可以把数字转换为Complex类对象,那么可以做相反的转换吗?如下:
cpp
Complex c4 = Complex(5);
int n = c4; //这样可以吗?
有办法可以这样做,但不是使用构造函数。构造函数只用于从某种类型到类类型的转换。要进行相反的转换,必须使用特殊的C++运算符函数--类型转换函数。
类型转换函数是把类类型转成其它类型的函数, 之后可以隐式或显示的进行相应的类型转换。
类型转换函数原型:
cpp
operator 类型名()const;//这个类型名是需要转成的类型名,如int
●类型转换函数必须是类成员函数; //把类转为其它类型,当然是类成员函数
●类型转换函数不能指定返回类型; //返回的类型在类型名中已经指定
●类型转换函数不能有参数 . //类型转换,不需要其它参数
例如,把Complex转换为int类型的函数如下:
cpp
operator int()const;//转换函数,把Complex转换为int,只返回实部。类成员函数
定义如下:
cpp
Complex::operator int()const//转换函数,把Complex转换为int
{
return real;//返回实部
}
测试如下:
cpp
Complex c4 = Complex(5,1);
int n1 = c4;
int n2 = (int)c4;
int n3 = int(c4);
cout << c4;
cout << n1 << "," << n2 << "," << n3 << endl;
其中n1是隐式转换,n2,n3都是显式转换,总之通过类型转换函数可以把Complex转成int。
在C++11中可将类型转换函数声明为显示的explicit。如下:
cpp
explicit operator int() const;//显式类型转换函数,把Complex转换为int,只返回实部
在调用时不能再进行隐式转换
cpp
Complex c4 = Complex(5,1);
//int n1 = c4; //错误,不能进行隐式转换
int n2 = (int)c4; //可以
int n3 = int(c4); //可以
警告:应谨慎地使用隐式类型转换函数。通常,最好选择是显式调用才会执行的函数。
总结,C++为类提供了下面的类型转换:
●只有一个参数的类构造函数用于将参数类型转换为类类型。如将int转换为Complex类型。
●类的成员函数--类型转换函数,用于将类类型转换为其它类型。如将Complex转换为int。
●上述两种转换最好都是显式转换,即在函数声明前加上explicit。