Effective C++ tips day1

来源:互联网 发布:淘宝直通车有用么 编辑:程序博客网 时间:2024/06/06 21:02

        这个专栏文章主要是读Scott Meyers写的《Effective C++》所记的笔记,等这个系列写完之后,将会读《More Effective C++》,因为是笔记,所以可能会比较杂乱~大笑


        1.extern关键字

        extern关键字主要有两种应用场景:1)extern “C” void fun(int a, int b);2)修饰函数或变量,从而声明函数或全局变量的作用范围。

        1)extern "C" void fun(int a, int b);

        该语句将告诉编译器,按照C的规则去翻译相应的函数名,而不是按照C++的规则。因为C++支持函数重载和其他原因,所以如果直接按照C++规则来进行翻译得到的结果将会与C规则得到的结果完全不同,因而LINK程序也就定位不到该函数了。具体原因可以参考C++编译器的函数名修饰规则。

        2)修饰函数或变量

        这里我们主要关注extern对于变量的修饰。当然,这里指的是全局变量,因为局部变量在运行时才会在堆栈中分配内存。而我们在外部声明全局变量时,extern关键词是必须的,比如int a,即便没有显示的初始化,但仍然是对于变量的定义。先举几个例子来看看什么是定义,什么是声明:

        extern int a;    //声明一个全局变量a

        int a;    //定义一个全局变量a

        extern int a = 0;    //定义一个全局变量a并给初值

        int a = 0;    //定义一个全局变量a并给初值
        下面对在头文件中使用extern进行说明。

        我们知道,一般在头文件中存放关于函数,变量,类的“声明”,而不要随意往头文件中定义什么东西,比如全局变量:

        #ifndef _XX_头文件.h

        #define _XX_头文件.h

        int a;

        #endif

        很糟糕的是,这里的a是全局变量的定义,如果该文件被多次引用,则a会被重复定义。虽然上面有#ifndef条件编译语句,所以能够保证你的头文件只被引用一次,但是由于宏名的有效范围仅限于本c源文件,因此如果多个c文件包含这个头文件的话,仍然会报错,当然,这时是在链接时报错,而不是编译时报错而已。


        2.explicit构造函数

        explicit关键字修饰构造函数,作用是阻止构造函数被用于执行隐式类型转换,当然它们仍然可以被用来进行显示类型转换。比如:

       

class B {public:        explicit B(int x = 0, bool b = true);}void doSomething(B bObject);    //函数,接受一个类型为B的对象B bObj1;doSomething(bObj1);    //OKB bObj2(28);    //OKdoSomething(28);    //Wrong! 因为doSomething应该接受一个B类型对象,而不是int,且int至B之间没有隐式转换doSomething(B(28));    //OK,使用B构造函数将int显示转换

        因为explicit禁止编译器执行非预期的类型转换,所以除非有一个好理由允许构造函数被用于隐式类型转换,否则我们会把它声明为explicit。


        3.copy构造和copy assignment

        我以前一直把这两个分别叫做拷贝构造函数和赋值构造函数,看了《Effective C++》之后发现我的叫法错了......准确地说,两者分别叫做拷贝构造函数和拷贝赋值操作符。这样的叫法与其用法很贴近:拷贝构造函数被用来“以同型对象初始化自我对象”;而拷贝赋值操作符则被用来“从另一个同型对象中拷贝其值到自我对象”。嗯,这样的名字就很方便我们理解它们的区别了:

class Widget {public:        Widget();    //default构造函数        Widget(const Widget& rhs);    //copy构造函数        Widget& operator=(const Widget& rhs);    //拷贝赋值操作符        ...}Widget w1;    //调用default构造函数Widget w2(w1);    //调用拷贝构造函数w1 = w2;    //调用拷贝赋值操作符Widget w3 = w2;    //调用拷贝构造函数

        我们可以很容易地区分这二者,如果一个新对象被定义(如以上语句的w3),一定会有构造函数被调用,不可能调用赋值操作。如果没有新对象被定义(如前面的"w1 = w2"语句),则不会有构造函数被调用,那么当然是赋值操作。

        此外,值得注意的是,拷贝构造函数定义了一个对象如何passed by value。比如:

bool hasAcceptableQuality(Widget w);...Widget aWidget;if (hasAcceptableQuality(aWidget)) ...
        上面的例子中,参数w是以by value的方式传递给hasAcceptableQuality,所以在上述调用中aWidget被复制到w体内。这个复制动作由Widget的copy构造函数完成。Pass-by-value意味着“调用拷贝构造函数”。以by value传递用户自定义类型常常是个坏注意,Pass-by-reference-to-const往往是比较好的选择。


        4.尽力避开不明确行为

        不明确(未定义)行为的结果是不可预期的,很可能让人不愉快。比如我们对一个null指针取值,或者利用指针指向一个无效的数组索引,都会导致不明确行为:       

int* p = 0;std::cout << *p;char name[] = "Darla";    //大小为6,别忘了最末尾的nullchar c = name[10]
       

        5.命名习惯
        这里主要以“指向一个T型对象”的指针命名为pt,意思是"pointer to T"。如下:

Widget* pw;class Airplane;Airplane* pa;class GameCharacter;GameCharacter* pgc;

        6.关于线程   

        作为一个语言,C++对线程没有任何意念——事实上它对任何并发(concurrency)事物都没有意念。所以,我们在使用线程是需要谨慎,起码要比不使用线程时注意一点点。


        7.TR1和Boost

        TR1即Technical Report 1,它是一份规范,描述加入C++标准程序库的诸多新机能。所有TR1组件都被置于命名空间tr1内,后者嵌套于命名空间std内。

        Boost是个组织,也是一个网站(http://boost.org),提供可移植、同僚复审、源码开放的C++程序库。大多数TR1机能以Boost的工作为基础,或者说Boost提供比TR1更多的东西,所以无论如何值得了解它。


        8.视C++为一个语言联邦

        一开始,C++只是在C上面加上一些面向对象的特性,这可以从C++最初的名称C with Classes中看出来。后来,当C++逐渐成熟之后,它开始接受不同于C with Classes的各种观念、特性和编程战略。今天,C++已经成为一个多重范型变成语言(multiparadigm programming language),一个同时支持过程形式(procedural)、面向对象形式(object-oriented)、函数形式(functional)、泛型形式(generic)、元编程形式(metaprogramming)的语言。

        各种能力和弹性使得C++成为一个无可匹敌的工具,但同时也可能引发某些迷惑:所有”适当用法“似乎都有例外,我们应该如何理解这样一个语言?

        最简单的方法就是将C++视为一个由相关语言组成的联邦而非单一语言。在它的某个次语言(sublanguage)中,各种守则与通例都倾向于简单、直观易懂、并且容易记住。然而当我们从一个次语言转向另一个次语言,守则可能改变。

        C++的次语言总共只有四个:

        1)C。

        2)Object-Oriented C++。

        3)Template C++。

        4)STL。

        当我们从一个次语言切换到另一个,导致高效编程守则要求我们改变策略时,不要惊讶。举个例子,对于内置类型(即C-like,比如int)类型而言,pass-by-value通常比pass-by-reference高效,但是当我们从C part of C++移往Object-Oriented C++,由于用户自定义构造函数和析构函数的存在,pass-by-reference-to-const往往更好。运用Template C++时尤其如此,因为这时我们甚至不知道所处理的对象的类型。然而当跨入STL之后,我们就会了解,迭代器和函数对象都是建立在C++指针之上,所以对STL的迭代器和函数对象而言,旧式的C pass-by-value再次适用。

        对比一下传值、传指针和传引用:

        1)传值。在内存中开辟一块内存空间,构造一个对象,然后将实参复制过来。

        2)传指针。传的是指针,指针指向地址,所以,当我们在函数中要使用到这一对象时,先要从指针解析出地址,然后才能根据地址找到对象。

        3)传引用。传的是地址。当我们在函数中要使用到这一对象时,直接按照地址找到对象即可。


        每天学习C++理念与方法,下期再见~大笑








原创粉丝点击