C++ Primer 学习笔记 — 隐式类类型转换

本文详细解析了C++中explicit关键字的作用与用法,通过实例展示了如何使用explicit防止不必要的类型隐式转换,并讨论了在何时及为何应当使用explicit。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.了解

explicit用来防止由构造函数定义的隐式转换。要明白它的作用,首先要了解隐式转换:可以用单个实参来调用的构造函数定义了从形参类型到该类类型的一个隐式转换。

#include <string>
#include <iostream>

using namespace std;

class Fruit               //定义一个类,名字叫Fruit
{
   public:
	
        Fruit(){}
		
		Fruit(const string &nst, const string &cst = "green"):name(nst),colour(cst){}  //构造函数
 
		bool isSame(const Fruit &otherFruit)   //期待的形参是另一个Fruit类对象,测试是否同名
		{
			return name == otherFruit.name;
		}
		
		void print()              //定义一个输出名字的成员print()
		{
			cout<<colour<<" "<<name<<endl;
		}
    
	private:
		string name;     //定义一个name成员           
		string colour;   //定义一个colour成员

};

int main()
{
	Fruit apple("apple","green");
 	Fruit orange("orange","yellow");
	cout<<"apple = orange ?: "<<apple.isSame(orange)<<endl;  //没有问题,肯定不同
	cout<<"apple = \"apple\" ?:"<<apple.isSame(string("apple"))<<endl; //用一个string做形参? 
 
    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"))<<endl;
   参考下例 :Fruit apple = Fruit("apple","green");  //定义一个Fruit类对象apple。
   也就是这样转换的。不过这就叫显式转换了,我们不标出来,系统帮我们完成的,叫隐式的。这里要说的是,假如你显示转换就可以不管有多少参数了,比如在前面提到的必须需要两个参数的构造函数时的例子。

#include <string>
#include <iostream>

using namespace std;

class Fruit               //定义一个类,名字叫Fruit
{
   public:
	
        Fruit(){}
		
		Fruit(const string &nst, const string &cst):name(nst),colour(cst){}  //构造函数
 
		bool isSame(const Fruit &otherFruit)   //期待的形参是另一个Fruit类对象,测试是否同名
		{
			return name == otherFruit.name;
		}
		
		void print()              //定义一个输出名字的成员print()
		{
			cout<<colour<<" "<<name<<endl;
		}
    
	private:
		string name;     //定义一个name成员           
		string colour;   //定义一个colour成员

};

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;
}


2.C++ Primer总结

class Sales_item
{
public: 
    Sales_item(const std::string & book);
    Sales_item(std::istream & is);
};

bool same_isbn(Sales_item book)
{
}

std::string null_book = "9-999-99999-9";
item.same_isbn(null_book);
item.same_isbn(cin);

        使用一个string类型对象作为实参传给same_isbn函数,编译器使用接收一个string的Sales_item构造函数从null_book生成一个新的Sales_item对象,新生成的(临时的)Sales_item被传递给same_isbn函数。
        将cin隐式转换为Sales_item。这个转换执行接收一个istream的Sales_item构造函数。该构造函数通过读标准输入来创建一个(临时的)Sales_item对象。然后该对象被传递给same_isbn函数。
         通过将构造函数声明为explicit,可以防止在需要隐式转换的上下文中使用构造函数:

class Sales_item
{
public: 
    explicit Sales_item(const std::string & book);
    explicit Sales_item(std::istream & is);
};
explicit关键字只能用于类内部的构造函数声明上。在类的定义体外部所做的定义上不再重复它。
// error: explicit只能用于类内部声明上
explicit Sales_item::Sales_item(std::istream & is)
{
    is >> *this;
}
现在,两个构造函数都不能用于隐式地创建对象,前两个使用都不能编译
item.same_isbn(null_book); // error
item.same_isbn(cin); // error

3.总结

       C++ Primer中提到:通常,除非有明显的理由想要定义隐式转换,否则,但形参构造函数应该为explicit。将构造函数设置为explicit可以避免错误,而且当转换有用时,用户可以显式地构造对象。
       

       google的c++规范中提到:explicit的优点是可以避免不合时宜的类型变换,缺点无。所以google约定所有单参数的构造函数都必须是显示的,只有极少数情况下拷贝构造函数可以不声明称explicit。例如作为其他类的透明包装器的类。

        effective c++中提到:被声明为explicit的构造函数通常比其non-explicit兄弟更受欢迎。因为它们禁止编译器执行非预期(往往也不被期望)的类型 转换。除非我有一个好理由允许构造函数被用于隐式类型转换,否则我会把它声明为explicit。我鼓励你遵循相同的政策。

4.参考文献

http://www.cnblogs.com/xushjie/archive/2012/09/23/2698590.html

http://blog.youkuaiyun.com/vagrxie/article/details/1586340

http://www.cnblogs.com/likebeta/archive/2012/07/31/explicit.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值