首页 > 代码库 > Effective C++ 26,27,28
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的对象再使用,而编译器不知道应该使用哪种方法。这是一个潜在的二义性,其一般情况下正常无误。
还有更简单一点的二义性,当两个函数重载时,如 f(int ) f(char);,而传入一个double类型的参数d时,就会发生二义性,编译器不知道应该将d转换为char类型还是int类型。
多继承中充满了二义性的可能。如多个基类具有同名的函数或数据成员,就必须指明成员的基类来消除二义性,即使一些成员在其所在的基类中是私有的,即派生类无法访问,但这还是有二义性的错误。为什么消除 对类成员的引用产生的二义性时不考虑访问权限:改变类成员的访问权限不应该改变程序的含义,即当类定义好之后,改变成员的访问属性会导致一些访问出错或这依旧正常,这是正确的,而如果改变访问属性改变了程序的含义(如A 类B类派生 出C类,c中一个函数调用两个基类中同名的成员,如果 消除二义性时考虑了访问权限, 当A中成员为公有,b中成员为私有时,c中函数调用a中成员,而改变了访问权限,a中为私有,b中为共有,就会导致c中函数调用b中成员,改变了程序的含义,但是对于程序猿,却一无所知。如果保护考虑的话,会出现访问出错,会提示程序员进行修改) 所以最好还是显示的消除二义性。
27.如果不想使用隐式生成的函数就要显式的禁止它。
对于这些不想使用的函数禁止它,原因很简单,为了之后更容易更新和维护,当你现在在写这个类时,你没有禁止这些不应该使用的函数,你记得这些事情,所以你自己不会犯这些错误,但当过了很长一段时间,或者其他人接替了你的工作来对你的代码进行升级和维护时,这个没有禁止的功能就很有可能出现在一些地方让你措手不及。所以最好还是禁止这些不应该使用的函数。
对于类中一些函数,编译器会隐式的自动生成,如缺省的无参构造函数,赋值 =,复制构造函数等,要禁止这些函数,一个简单的方法就是声明这些函数为private ,显示声明防止编译器自动生成,而private防止其他人调用。
但这样还是不安全,成员函数和 友元函数还是可以调用这些私有函数。这样就只去声明这些函数,而不去定义函数体,当其他函数调用时,编译器就会在链接时报错。
28.划分全局名字空间。
使用命名空间来解决命名冲突。对命名空间之前有过一些研究:c++命名空间,命名空间是可以嵌套的。
命名空间的使用方式有三种, using namespace xxx;之后所有的xxx命名空间的成员都可以直接访问。 using xxx::成员 ,对于xxx命名空间中的这个成员可以直接使用。xxx::成员,在一个地方使用xxx命名空间中的这个成员。
可以使用struct来近似实现namespace , 先创建一个结构用以保存全局符号名,然后将这些全局符号名作为静态成员放入结构中。访问时加上struct的名作为前缀 ,与命名空间类似。
struct sdm{ static const double VERSION ; class A{ public: void f(){cout<<"SDM"<<endl;} }; }; const double sdm::VERSION = 1.0; class A{ public: void f(){cout<<"GLOBEL"<<endl;} }; int main(){ cout<<sdm::VERSION; sdm::A sdma; A a;对于类型名,可以显式地去掉空间引用:
typedef sdm::A A;对于函数,一般返回一个函数的常指针:
sdm::A& (* const getA)()= sdm::getA; sdm::A& (& getA)() = sdm::getA;//返回函数的引用
Effective C++ 26,27,28