Effective C++学习第一天

1:区分C++中的术语声明、定义、初始化的概念

         声明(declaration):告诉编译器某个东西的名称和类型,但略去其他细节(可以出现多次,编译器不分配内存)。

         定义(definition):提供编译器一些声明式所遗漏的细节,编译器此时为对象分配内存(注意内置类型和自定义类型定义的变量都叫做对象,变量有且仅有一次定义)。

         声明和定义:1)extern关键字区分;2)声明中出现初始化式,当做定义,即使出现extern关键字;3)带{ }的是定义,其他的是声明;

         引申:头文件中不要放入变量定义,容易导致重复定义错误;可以使用static关键字把变量定义限制在源文件作用域内;

头文件中一般不放实体定义,但有三个例外:const常量定义;类的定义;inline函数;

         如何区分声明和定义,可以参考这篇文章:https://blog.csdn.net/sjxbf/article/details/6310150;

         初始化(initialization):给予对象初值的过程;用户自定义函数对象初始化由构造函数执行,default构造函数可以调用不带任何实参(通常有两种情况:情况1:没有参数;情况2:每个参数都有缺省值)

2:explicit关键字:用来阻止执行隐式类型转换,但仍可以执行显示类型转换;  

         explicit主要用于单个参数的类构造函数中,如果出现多个参数,explicit关键字失效,除了一种情况例外,就是除了第一个参数,其他参数都有默认缺省值时,explicit依然有效;

3:copy构造函数和copy assignment操作符

         copy构造函数:用于同型对象初始化自我对象时(初始化);

         copy assignment操作符:用于从一个同型对象中拷贝其值到自我对象(赋值操作时);

        copy构造函数的几个运用场所:1)初始化时,如Wiget w2;Wiget w1(w2);2)初始化赋值时;Wiget w2=w1;3)类对象值传递的时候;

4:命名习惯:养成选用所实现目标的英文翻译是个不错的选择,可以方便阅读和修改;例如类中的左右操作数可以定义参数名称为lhs(left-hand-side)和rhs(right-hand-side);

5:合理利用TR1(class template和function template,hash table,reference-counting smart pointers,regular expressions)和boost网站资源(提供可移植,同僚复审,源码开发的C++程序库)

6:C++语言是一个多重范型编程语言(multiparadigm programming language),一个同时支持过程形式(procedural)、面向对象形式(object-oriented)、函数形式(functional)、泛型形式(generic)、元编程形式(metaprogramming)的语言。C++可以视为由相关语言组成的联邦和非单一语言,其中每个次语言中,各种守则与通例都倾向于简单直观易懂和容易记住,关键是次语言之间切换过程。次语言分为一下几个方面:

         1)C:区块(blocks),语句(statement)、预处理器(preprocessor)、内置数据类型(bulit-in data types)、数组(arrays)、指针(pointers),但在高效编程时C语言却没有C++的功能如模板(template)、异常(exceptions)、重载(overloading)等;

      2)object-oriented C++:classes(构造和析构函数)、封装(encapsulation)、继承(inheritance)、多态(polymorphism)、virtual函数(动态绑定);类的三个核心:封装,继承,多态;

          3)Template C++。C++泛型编程的思想;

         4)STL。STL是个template程序库,它将容器(containers)、迭代器(iterators)、算法(algorithm)以及函数对象(function objects)联合起来;

7:尽量使用const,enum,inline替代#define(用编译器替代预处理器)

       #define 是在程序预处理的时候执行的,它不被看成语言的一部分,也就可能会出现所使用的宏变量没有进入到记号表(symbol table)中,修改的方式:用一个常量替代宏;另外使用const还有一个好处,系统会做类型检查,同时完成类型的位数限制(发生在float中)如#define pi 3.141592653;和const float pi=3.141592653得到的结果不一样,相比之下,用const会产生更小量的码值;

    # define并不注重作用域(scope)的概念,一旦定义,除非#undef,后面都有效,因此不能用于定义类的专属常量,也不能提供任何封装性;但const却能提供这样的功能,const可以将常量的作用域限制在classes内,让它成为class的一个成员,为了确保常量最多有一份实体,你得设置为static成员,如:

 class gameplayer{

private:

          static const int Numturns=5;//常量声明式,和以往static声明和定义不一样,这种成为in-class初值设定                                                                           //只允许对整形变量使用,还和C++编译器是否支持有关

         //static const double score;一般的static常量声明

          int score[Numturns];//使用常量

};

const double gameplayer::score=95.0;//static常量定义

类中的Numturns是一个声明式而非定义式,C++要求你对你所使用的任何东西提供定义式,但如果它是类的一个专属常量而且还是整形类型(int,char,bool),则需要特殊处理。只要不取地址,可以直接声明并使用,不需要提供定义式;但如果你取了某个类专属常量的地址或者编译器需要看到一个定义式,则你必须提供定义式:const int gameplayer::Numturns;//声明时已经赋值,定义则不需要赋值(放入实现文件而非头文件中)

      如果编译器不允许static整数型class常量完成in-class初值设定,可以用the enum hack补偿方法完成同样功能;

 class gameplayer{

private:

          enum {Numturns=5};

          int score[Numturns];//使用常量

};

    引申:enum hack知识:enum hack的行为有点像#define而非const,因为其和#define同样不能取地址,而const可以,如果不想让别人获得一个指针或者引用指向你的某个常量,enum可以实现这个功能;此外,enum hack是template metaprogramming(模板元编程)的基础技术;

     #define常见的误用情况是用它实现宏(macros),宏看起来像函数,却不会招致函数调用带来的额外开销,这也是错误的根源,如:

     #define CALL_WITH_MAX(a,b)  f((a)>(b)?(a):(b))

     int a=5;b=0;CALL_WITH_MAX(++a,b);//a被累加两次   

     解决方案:使用template inline函数,会在被调用处代码展开,省去参数压栈、栈帧开辟和回收、结果返回;同时会做安全检查或者自动类型转换;

     inline void callwithmax(const int&a,const int&b){f(a>b?a:b);}

     结论:对于单纯变量,最好以const对象或者enum替换#define;

               对于函数的宏,最好改用inline函数替换#define;

8:尽量多使用const:它允许你指定一个语义约束,而编译器执行这个约束,它允许你告诉编译器和其他程序员某值应该保持不变;

     1)STL迭代器中:声明迭代器为const,则迭代器指针不能变化,声明迭代器内容为const,如const_iterator,则迭代器内容不可变;

    2)在函数声明式中,const可以和返回值、各参数、函数本身产生关联;令函数返回一个常量值,往往可以降低因用户错误而造成的意外,同时不放弃安全性和高效性,如无意中输入的if(a*b=c)操作,可通过返回值设为const来避免这个错误;

    3)const成员函数:设置成const好处(方便理解;使操作const对象成为可能,编程更加高效);如果两个成员函数只是常量(constness)不同,可以被重载,这是C++的一个特性;

      引申:如果函数返回值是一个内置类型,那么改动函数返回值是不合法的,C++以by value返回对象意味着改动的只是对象的一个副本,而不是本身,这并不是我们希望的,因此需要把其设置为引用;

     成员函数是const意味着什么?有两种说法:bitwise constness(physical constness)和logical constness;bitwise constness认为成员函数不改变对象中的任何成员变量的任何一个bit,因此编译器只需要寻找有无成员变量赋值操作,bitwise constness这一个特性是C++对常量性(constness)的定义,因此const成员函数不能改变对象中的任何non-static成员变量,但是在指针的作用下这个const会发生问题,如:

    const mystring a("hello world"); char *pc=&a[0];*pc='j';//改变了const特性

   logical constness:一个const成员函数可以修改它处理对象内的某些bits,但只有在客户端侦查不出的情况下才能如此,如何解除编译器中的bitwise constness特性呢?可以使用const相关的一个关键字mutable,mutable可以释放non-static成员变量中的bitwise constness约束;

 4)在const和non-const中避免重复的方法:用其中一个调用另外一个,通过我们采用常量性转移,如;

class textblock{

public:

      const char&operator[](std::size_t position)const     //const 成员函数不变

     {

     }

   char& operator[](std::size_t position)

   {

      return const_cast<char&>(//去除返回值const

            static_cast<const textblock&>(*this)//this指针添加const

              [position]);

   } 

};

    采用non-const调用const版本的函数来避免重复,而不是相反的做法,原因:const变为non-const过程中可能会使得const变量发生变化,这与我们预期不合;而non-const本身就是可变的,不用担心这个现象的发生;

结论:声明const可以帮助编译器侦查出错误的用法;

         编译器强制实行bitwise constness,但可以通过指针方式改变;

        const和non-const成员函数有着实质等价的实现时,可以用non-const调用const来避免代码重复;

    





         

猜你喜欢

转载自blog.csdn.net/xx18030637774/article/details/80751795