参考
C++ Primer Plus (第6版)
类自动转换
接受一个参数的构造函数允许使用赋值语法将对象初始化一个值
Classname object = value;
等价于
ClassName object(value);
等价于
ClassName object = ClassName(value);
只有接受一个参数的构造函数才能作为转换构造函数(某类型->类)
#include <iostream>
class Test{
private:
int m_a;
double m_b;
public:
Test();
Test(double b); // 可作为转换构造函数
// Test(int a, double b = 0); // 可作为转换构造函数
Test(int a, double b); // 不可作为转换构造函数
Test(const Test& t);
Test& operator=(const Test& t);
friend void Display(const Test& t, int n);
};
Test::Test(){
std::cout << "构造 Test()" << std::endl;
m_a = 0; m_b = 0;
}
Test::Test(double b){
std::cout << "构造 Test(double)" << std::endl;
m_a = 0;
m_b = b;
}
Test::Test(int a, double b){
std::cout << "构造 Test(int, double)" << std::endl;
m_a = a;
m_b = b;
}
Test::Test(const Test& t){
std::cout << "复制构造 Test(const Test&)" << std::endl;
m_a = t.m_a;
m_b = t.m_b;
}
Test& Test::operator=(const Test& t)
{
std::cout << "operator=" << std::endl;
if(this == &t)
{
return *this;
}
m_a = t.m_a;
m_b = t.m_b;
return *this;
}
void Display(const Test& t, int n){
for(int i = 0; i < n; i ++){
std::cout << t.m_a << "," << t.m_b << std::endl;
}
}
int main(){
Test test(10); // int 转为 double, 使用Test(double b)
std::cout << "---------" << std::endl;
test = 12; // int 转为 double, 使用Test(double b)
std::cout << "---------" << std::endl;
Test test1 = 11; // int 转为 double, 使用Test(double b)
Test test2 = test1;
Display(422, 2); // 遇到int类型, 找不到Test(int a), 寻找其它内置类型(int可以转换)的构造函数, Test(int b) 满足要求
return 0;
}
注意, 当且仅当转换不存在二义性时, 才会进行转换, 如果这个类还定义了构造函数Test(long), 则编译器会报错
explicit 可以关闭隐式转换
explicit Test(double b); // 不支持隐式转换
类强制转换
自动转换把 某类型 转成 类
强制转换把 类 转成 某类型, 需要用到特殊C++运算符函数, 转换函数
转换函数 是 用户定义的强制类型转换, 可以显示和隐式的调用(类->某类型)
Test t(10.2);
double a = double (t);
double b = (double) t;
Test tt(10, 3);
double aa = tt; // 隐式转换, 编译器会查看是否定义于此匹配的转换函数, 没有则生成错误消息
转换函数
operator typeName(); // typeName 是要被转换的类型
注意:
- 必须是类方法
- 不能指定返回类型
- 不能由参数
#include <iostream>
class Test{
private:
int m_a;
double m_b;
public:
Test();
Test(double b);
Test(int a, double b);
// 转换函数
operator int() const;
operator double() const;
};
Test::Test(){
m_a = 0; m_b = 0;
}
Test::Test(double b){
m_a = 0;
m_b = b;
}
Test::Test(int a, double b){
m_a = a;
m_b = b;
}
Test::operator double() const {
return m_a + m_b;
}
Test::operator int() const{
return int(m_a + m_b+ 0.5);
}
int main(){
Test test(9, 2.8);
double p = test;
cout << "转换成double = " << p << std::endl;
cout << "转换成int = " << int(test) << std::endl;
long g = (double) test; // 使用double 转换
long gg = int(test) // 使用int 转换
}
转换函数有时候也不需要隐式转换
int ar[20]
...
Test t(120, 2); // 这个Test只有一个转换函数operator int(), 不然ar[t] 这里转换会有二义性
...
int T = 1;
...
cout << ar[t] << endl; // 写错的地方
原本是要把T作为数组索引, 不小心写错了, 写成对象t, Test类定义了operator int() , 会把转换成122, 作为数组索引, 然后就导致越界
原则上最好使用显示转换, 避免隐式转换
C++98 explicit不能用于转换函数, C++11消除这个限制
class Test{
...
explicit operator int() const;
explicit operator double() const;
};
也可以用功能相同的非转换函数替换转换函数, 仅在显示调用
class Test{
...
int Test_to_int() const {return int(a + b);}
};
总结
- 只有一个参数的类构造函数 用于类型于参数相同的值 -> 类类型, 构造函数声明中使用explict可防止隐式转换, 只允许显示转换
- 转换函数是特殊的类成员运算函数, 可将 类 -> 其它类型, 是类成员, 没有返回值, 没有参数, 名为operator typeName(), C++11可以用explict防止隐式转换, 只允许显示转换
转换函数与友元函数
给没有转换函数和构造转换函数的Test类重载加法运算符, 重载同一运算符成员函数和友元函数不能都提供
// 成员函数
Test test::operator+(const Test& s) const {
Test t(m_a + m_b + s.m_a + s.m_b);
return t;
}
// 友元函数
Test operator+(const Test& t1, const Test& t2) const {
Test t(t1.m_a + t1.m_b + t2.m_a + t2.m_b);
return t;
}
第一种情况: a 和 b 都是类
Test a(9, 12);
Test b(12, 8);
Test t;
t = a + b;
// Test类用的是 成员函数 的话
// t = a + b ---转换---> t = a.operator+(b)
// Test类用的是 友元函数 的话
// t = a + b ---转换---> t = operator+(a, b)
这个 成员函数, 友元函数 都可以实现
现在给Test类提供Test(double)构造转换函数
第二种情况: a是类, b不是类, a在前面
Test a(9, 12);
double b = 13.6;
Test t;
t = a + b;
// Test类用的是 成员函数 的话
// t = a + b ---转换---> t = a.operator+(Test(b))
// Test类用的是 友元函数 的话
// t = a + b ---转换---> t = operator+(a, Test(b))
这个 成员函数, 友元函数 都可以实现
第三种情况: a是类, b不是类, b在前面
Test a(9, 12);
double b = 13.6;
Test t;
t = b + a;
// Test类用的是 成员函数 的话 不行, 因为b.operator+()的参数要double类型
// Test类用的是 友元函数 的话
// t = b + a ---转换---> t = operator+(Test(b), a))
这个只有 友元函数 都可以实现
要成员函数也可以实现, 可以再加一个友元函数
Test operator+(double x); // 成员函数, 满足 Test + double
friend Test operator+(double x, Test& t); // 友元函数, 满足 double + Test