C++ primer 中有这么一句话:可以用单个实参来调用的构造函数定义了从形参类型到该类型的一个隐式转换。
这么解释:
比如有个类A的对象a的成员函数的参数应该是类A的对象,但是把一个别的类型B的对象b传进去了,而且这个对象b的类型恰好是A的单参数构造函数参数类型,这时系统就用这个b自作聪明的创建了一个类A的临时对象c,虽然c和a都是A类型,但是是不同的对象。
这种隐式转换有很大风险,可以用explicit加在单参数构造函数前来避免这种类类隐形转换。
我个人认为这就是一种人为使用错误,而编译器又恰恰没有指出。
本来应该A.a(A(b)),凭什么用成A.a(b)?哈哈……
#include <string>
#include <iostream>
using namespace std;
class Fruit //定义一个类,名字叫Fruit
{
string name;//定义一个name成员
string colour;//定义一个colour成员
public:
bool isSame(const Fruit& otherFruit)//期待的形参是另一个Fruit类对象,测试是否同名
{
return name == otherFruit.name;
}
void print()//定义一个输出名字的成员print()
{
cout << colour << " " << name << endl;
}
Fruit(const string& nst, const string& cst = "green"):name(nst), colour(cst){}
Fruit(){}
};
int main()
{
Fruit apple("apple");
Fruit orange("orange");
cout << "apple = orange? : " << apple.isSame(orange) << endl;
//隐式转换
cout << "apple = \"apple\"? :" << apple.isSame(string("apple")) << endl;
return 0;
}
你会发现最后的使用上,我们用一个string类型作一个期待Fruit类形参的函数的参数,结果竟然得出了是true(1),不要感到奇怪,这就是我现在要讲的东西,隐式类类型转换:“可以用单个实参来调用的构造函数定义了从形参类型到该类型的一个隐式转换。”(C++ Primer)
首先要单个实参,你可以把构造函数colour的默认实参去掉,也就是定义一个对象必须要两个参数的时候,文件编译不能通过。然后满足这个条件后,系统就知道怎么转换了,不过这里比较严格
以前我们构造对象的时候Fruit apple("apple")其实也已经有了一个转换,从const char *的C字符串格式,转为string,在这里,你再apple.isSame("apple")的话,蠢系统不懂得帮你转换两次,所以你必须要用 string()来先强制转换,然后系统才知道帮你从string隐式转换为Fruit,当然其实你自己也可以帮他完成。 cout<<"apple = \"apple\" ?:"<<apple.isSame(Fruit("apple"));这样。Fruit apple = Fruit("apple"); //定义一个Fruit类对象apple。也就是这样转换的。不过这就叫显式转换了,我们不标出来,系统帮我们完成的,叫隐式的呗。
这里要说的是,假如你显示转换就可以不管有多少参数了,比如在前面提到的必须需要两个参数的构造函数时的例子。
int main()
{
Fruit apple("apple", "green");
Fruit orange("orange", "yellow");
cout << "apple = orange? : " << apple.isSame(orange) << endl;
//显示转换
cout << "apple = \"apple\"? :" << apple.isSame(Fruit("apple", "green")) << endl;
return 0;
}
在你不想隐式转换,以防用户误操作怎么办?
C++提供了一种抑制构造函数隐式转换的办法,就是在构造函数前面加explicit关键字,你试试就知道,那时你再希望隐式转换就会导致编译失败,但是,要说明的是,显式转换还是可以进行,出于不提供错误源代码例子的原则,错误的情况就不提供了,自己试试吧
当一个类定义了一个有形参的构造函数后,在使用其他需要提供类对象的成员函数时,若没有在成员函数前如入强制显式转换,就可能发生在调用成员函数的时候提供一个有对应构造函数实参的对象,并由构造函数隐式的转换为一个临时类对象,并将临时对象传递给成员函数,当成员函数结束时,临时对象失效,造成错误.所以当不需要构造函数将其他对象隐式转换为类对象的时候,我们需要在构造函数前加上explicit进行限制,要求需要显式的使用构造函数转换其他对象为类对象.
具体的例子如下:
class Noname
{
public:
Noname( );
Noname(std::string & str1);
bool Dosomething(Noname)
{
}
private:
std::string str;
int ival;
};
此时调用Dosomething()时若传递一个string类的实参,编译器将自动调用Noname(std::string &str)创建一个临时对象并传递给Dosomething( );
若在构造函数前加上explicit的话,就不能直接将string作为实参传递给Dosometing(),而需要显示的调用构造函数来创建对象并传递给Dosomething( ),如:Noname.Dosometing( Noname(str1))
这么解释:
比如有个类A的对象a的成员函数的参数应该是类A的对象,但是把一个别的类型B的对象b传进去了,而且这个对象b的类型恰好是A的单参数构造函数参数类型,这时系统就用这个b自作聪明的创建了一个类A的临时对象c,虽然c和a都是A类型,但是是不同的对象。
这种隐式转换有很大风险,可以用explicit加在单参数构造函数前来避免这种类类隐形转换。
我个人认为这就是一种人为使用错误,而编译器又恰恰没有指出。
本来应该A.a(A(b)),凭什么用成A.a(b)?哈哈……
#include <string>
#include <iostream>
using namespace std;
class Fruit //定义一个类,名字叫Fruit
{
string name;//定义一个name成员
string colour;//定义一个colour成员
public:
bool isSame(const Fruit& otherFruit)//期待的形参是另一个Fruit类对象,测试是否同名
{
return name == otherFruit.name;
}
void print()//定义一个输出名字的成员print()
{
cout << colour << " " << name << endl;
}
Fruit(const string& nst, const string& cst = "green"):name(nst), colour(cst){}
Fruit(){}
};
int main()
{
Fruit apple("apple");
Fruit orange("orange");
cout << "apple = orange? : " << apple.isSame(orange) << endl;
//隐式转换
cout << "apple = \"apple\"? :" << apple.isSame(string("apple")) << endl;
return 0;
}
你会发现最后的使用上,我们用一个string类型作一个期待Fruit类形参的函数的参数,结果竟然得出了是true(1),不要感到奇怪,这就是我现在要讲的东西,隐式类类型转换:“可以用单个实参来调用的构造函数定义了从形参类型到该类型的一个隐式转换。”(C++ Primer)
首先要单个实参,你可以把构造函数colour的默认实参去掉,也就是定义一个对象必须要两个参数的时候,文件编译不能通过。然后满足这个条件后,系统就知道怎么转换了,不过这里比较严格
以前我们构造对象的时候Fruit apple("apple")其实也已经有了一个转换,从const char *的C字符串格式,转为string,在这里,你再apple.isSame("apple")的话,蠢系统不懂得帮你转换两次,所以你必须要用 string()来先强制转换,然后系统才知道帮你从string隐式转换为Fruit,当然其实你自己也可以帮他完成。 cout<<"apple = \"apple\" ?:"<<apple.isSame(Fruit("apple"));这样。Fruit apple = Fruit("apple"); //定义一个Fruit类对象apple。也就是这样转换的。不过这就叫显式转换了,我们不标出来,系统帮我们完成的,叫隐式的呗。
这里要说的是,假如你显示转换就可以不管有多少参数了,比如在前面提到的必须需要两个参数的构造函数时的例子。
int main()
{
Fruit apple("apple", "green");
Fruit orange("orange", "yellow");
cout << "apple = orange? : " << apple.isSame(orange) << endl;
//显示转换
cout << "apple = \"apple\"? :" << apple.isSame(Fruit("apple", "green")) << endl;
return 0;
}
在你不想隐式转换,以防用户误操作怎么办?
C++提供了一种抑制构造函数隐式转换的办法,就是在构造函数前面加explicit关键字,你试试就知道,那时你再希望隐式转换就会导致编译失败,但是,要说明的是,显式转换还是可以进行,出于不提供错误源代码例子的原则,错误的情况就不提供了,自己试试吧
当一个类定义了一个有形参的构造函数后,在使用其他需要提供类对象的成员函数时,若没有在成员函数前如入强制显式转换,就可能发生在调用成员函数的时候提供一个有对应构造函数实参的对象,并由构造函数隐式的转换为一个临时类对象,并将临时对象传递给成员函数,当成员函数结束时,临时对象失效,造成错误.所以当不需要构造函数将其他对象隐式转换为类对象的时候,我们需要在构造函数前加上explicit进行限制,要求需要显式的使用构造函数转换其他对象为类对象.
具体的例子如下:
class Noname
{
public:
Noname( );
Noname(std::string & str1);
bool Dosomething(Noname)
{
}
private:
std::string str;
int ival;
};
此时调用Dosomething()时若传递一个string类的实参,编译器将自动调用Noname(std::string &str)创建一个临时对象并传递给Dosomething( );
若在构造函数前加上explicit的话,就不能直接将string作为实参传递给Dosometing(),而需要显示的调用构造函数来创建对象并传递给Dosomething( ),如:Noname.Dosometing( Noname(str1))