条款26: 当心潜在的二义性

class
B;        // 对类B提前声明
        //
class A {
public:
 A(const B&);    // 可以从B构造而来的类A
};

class B {
public:
 operator A() const;    // 可以从A转换而来的类B
};
void f(const A&);
B b;
f(b);            // 错误!——二义

一看到对f的调用,编译器就知道它必须产生一个类型A的对象,即使它手上拿着的是一个类型B的对象。有两种都很好的方法来实现(见条款M5)。一种方法是调用类A的构造函数,它以b为参数构造一个新的A的对象。另一种方法是调用类B里自定义的转换运算符,它将b转换成一个A的对象。因为这两个途径都一样可行,编译器拒绝从他们中选择一个。



类型转换函数的作用是将一个类的对象转换成另一类型的数据。如果已声明了一个Complex类,可以在Complex类中这样定义类型转换函数:
    operator double( )
    {
        return real;
    }
函数返回double型变量real的值。它的作用是将一个Complex类对象转换为一个double型数据,其值是Complex类中的数据成员real的值。请注意,函数名是operator double,这点是和运算符重载时的规律一致的。

类型转换函数的一般形式为:
    operator 类型名( )
    {
        实现转换的语句
    }
在函数名前面不能指定函数类型,函数没有参数。其返回值的类型是由函数名中指定的类型名来确定的。类型转换函数只能作为成员函数,因为转换的主体是本类的对象。不能作为友元函数或普通函数。



另一种类似的二义的形式源于C++语言的标准转换——甚至没有涉及到类:

void f(int);
void f(char);

double d = 6.02;
f(d);			// 错误!——二义

d是该转换成int还是char呢?两种转换都可行,所以编译器干脆不去做结论。幸运的是,可以通过显式类型转换来解决这个问题:

f(static_cast<int>(d));	// 正确, 调用f(int)
f(static_cast<char>(d));	// 正确, 调用f(char)

多继承(见条款43)充满了潜在二义性的可能。最常发生的一种情况是当一个派生类从多个基类继承了相同的成员名时:

class Base1 {
public:
 int doIt();
};

class Base2
{
public:
 void doIt();
};

class Derived: public Base1	// Derived没有声明
		public Base2 {	// 一个叫做doIt的函数
...
};

Derived d;
d.doIt();		// 错误!——二义

当类Derived继承两个具有相同名字的函数时,C++没有认为它有错,此时二义只是潜在的。然而,对doIt的调用迫使编译器面对这个现实,除非显式地通过指明函数所需要的基类来消除二义,函数调用就会出错:

d.Base1::doIt();	// 正确, 调用Base1::doIt
d.Base2::doIt();	// 正确, 调用Base2::doIt
class Base1 { ... };	// 同上
class Base2 {
private:
void doIt(); // 此函数现在为private
};

class Derived: public Base1, public Base2
{ ... };		// 同上

Derived d;
int i = d.doIt();	// 错误! — 还是二义!

对doIt的调用还是具有二义性,即使只有Base1中的函数可以被访问。另外,只有Base1::doIt返回的值可以用于初始化一个int这一事实也与之无关——调用还是具有二义性。如果想成功地调用,就必须指明想要的是哪个类的doIt。

参考:

http://see.xidian.edu.cn/cpp/biancheng/view/222.html

条款26: 当心潜在的二义性

时间: 2024-11-05 20:49:08

条款26: 当心潜在的二义性的相关文章

《Effective C++》条款26 防卫潜伏的ambiguity模棱两可的状态

每个人都有思想.有些人相信自由经济学,有些人相信来生.有些人甚至相信COBOL是一种真正的程序设计语言.C++也有一种思想:它认为潜在的二义性不是一种错误.ambiguity 这是潜在二义性的一个例子: class B; // 对类B提前声明// class A {public: A(const B&); // 可以从B构造而来的类A}; class B {public: operator A() const; // 可以从A转换而来的类B}; 这些类的声明没一点错--他们可以在相同的程序中共存

《Effective C++》之条款26:尽可能延后变量定义式的出现时间

<Effective C++> 条款26:尽可能延后变量定义式的出现时间 只要你定义了一个变量而其类型带有一个构造函数和析构函数,那么当程序的控制流到达这个变量定义式时,你便得承受构造成本:当这个变量离开作用域时,你便得承受析构成本.即使这个变量最终并未被使用,仍需耗费这些成本,所以你应该尽量避免这种情形. 对于"尽可能延后"的理解: 不只应该延后变量多的定义,直到非得使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初始实参为止.如果这样,不仅能够避免构造(析构

EC读书笔记系列之14:条款26、27、28、29

条款26 尽可能延后变量定义式的出现时间(Lazy evaluation) 记住: ★尽可能延后变量定义式的出现.这样做可增加程序的清晰度并改善程序效率 ---------------------------------------------------------------------- 举例说明: std::string encryptPassword( const std::string &password ) { using namespace std; string encrypt

Effective C++:条款26:尽可能延后变量定义式的出现时间

(一) 那么当程序的控制流到达这个变量定义时,变承受构造成本:当变量离开作用域时,便承受析构成本. string encryptPassword(const std::string& password) { using namespace std; string encrypted; if(password.length() < MinimumPasswordLengt) { throw logic_error("Password is too short") } -//

《Effective C++》学习笔记——条款26

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 杂谈: <Effective C++>已经看到第26个条款了,总共55条,看了也大约近一半了. 刚开始看这本书,还是因为当时想提高一下C++,然后搜了搜书想看一下, 当时不知道在哪看到了一句话: 学C++的人,分为两种读过这本书的和没读过这本书的.(话说当时还

《more effective c++》条款26 限制类对象的个数

问题: 如何限制类对象的个数?比如1个,10个等等. 方法(1): 将类的构造函数定义为private,那么就无法实例化这个类了.但是如何创建1个对象出来?方法有2种: 1.声明一个友元函数,那么在友元函数中就可以调用构造函数了,创建对象时使用static限制,那么就保证只有一个对象了.类似的定义如下: 1 class Printer 2 { 3 public: 4 friend Printer& thePrinter(); 5 private: 6 Printer(); 7 Printer(c

Effective C++ 条款26

尽可能延后变量定义式的出现时间 我们知道定义一个对象的时候有一个不争的事实,那就是分配内存.如果是我们自定义的对象,程序执行过程中会调用类的构造函数和析构函数. 我们打个比方,如果天下雨了,你带把雨伞肯定是值得的.但是,如果你带伞了,今天却没下雨,你是不是感觉自己亏了?的确,亏在了带了却没用,所以伞就变成了累赘. 本节的关键就在于此,如果你定义一个变量或者对象没有被使用,那么就是不完美的代码. 我们看一个代码片段: std::string encryptPassword(const std::s

Effective C++ 26,27,28

26.当心潜在的二义性. 一些潜在的二义性的例子: class A{ public: A(const B&); }; class B{ public: operator A() const; }; void f(const A&); 一般情况下,这样写不会出错,但当调用f函数传入一个 B的对象b时,就会发生二义性错误,b既可以通过A的构造函数获得一个A的对象,也可以通过B的类型转换运算符来将b变成一个A的对象再使用,而编译器不知道应该使用哪种方法.这是一个潜在的二义性,其一般情况下正常无误

Effective Modern C++翻译(5)-条款4

条款4:了解如何观察推导出的类型 那些想要知道编译器推导出的类型的人通常分为两种,第一种是实用主义者,他们的动力通常来自于软件产生的问题(例如他们还在调试解决中),他们利用编译器进行寻找,并相信这个能帮他们找到问题的源头(they're looking for insights into compilation that can help them identify the source of the problem.).另一种是经验主义者,他们探索条款1-3所描述的推导规则,并且从大量的推导情