Chinaunix首页 | 论坛 | 博客
  • 博客访问: 4798
  • 博文数量: 1
  • 博客积分: 1400
  • 博客等级: 上尉
  • 技术积分: 30
  • 用 户 组: 普通用户
  • 注册时间: 2009-05-12 18:26
文章分类
文章存档

2009年(1)

我的朋友
最近访客

分类: C/C++

2009-08-04 18:08:14

C++编程思想》阅读笔记

作者:八点半 时间:2009/7–8  Emailzuoan911@gmail.com

1、声明和定义的区别:声明是向编译器介绍标示符,extern表示变量声明而非定义;定义为标示符分配存储空间(内存)。

2、带空参数表的函数声明,CC++有很大不同:在C语言中,声明int func(); 表示一个可带任意参数(任意数目,任意类型)的函数,这将妨碍类型检查;而在C++中表示一个不带参数的函数

3、就名字空间包含来讲:

       #include   相当于

       #include

       using namespace std;  //std中封装了标准C++

4C预处理器的一个重要功能是可以进行字符数组的拼接,例如:

       cout<<"this is far too long to put on a"

                  "single line but it can be broken up with"

                  "no ill effects\n as long as there is no"

                  "array.\n";

5for循环语句的执行顺序:

              for(statement1; statement2; statement3)

                     statement4;

       顺序:statement1->statement2->statement4->statement3->statement2

6switch:选择器类型可为整型或字符型,bool型亦可。不可为实类型,floatdouble

       void foo(char* a, char* b, int len) {        

              switch (len & 0x7) {         

                     default:

                            while (len > 7) {        

                                    len -= 8; *a++ = *b++;                      

                     case 7: *a++ = *b++;        

                     case 6: *a++ = *b++;        

                     case 5: *a++ = *b++;        

                     case 4: *a++ = *b++;        

                     case 3: *a++ = *b++;        

                     case 2: *a++ = *b++;        

                     case 1: *a++ = *b++;        

                     }        

              }        

}  

switch不管有无括号,都会首先匹配case语句,此题匹配完后,case语句都位于while循环中,故会接着执行下去,知道退出while循环。来源:http://rednaxelafx.javaeye.com/blog/132053

7、头文件是存放接口规范的地方,编写头文件的基本原则是只限于声明,即只限于对编译器的信息,不涉及通过生成代码或创建变量而分配存储空间的信息。当然,在头文件中定义一个静态变量,也是正确的。

另外,不要在头文件中放置使用指令(using namespace std)

8、面向对象编程可总结为一句话:向对象发送消息。而技巧是设计对象和消息。

9、句柄类:a 隐藏实现;b 减少重复编译。实现方法:将类的private部分放在一个结构中,结构不在头文件中定义。

句柄类实现模板:

       #ifndef HANDLE_H

       #define HANDLE_H

       class Handle{

       private:

              struct Cheshire; //只声明

              Cheshire *smile; //使用指针访问,指针大小固定

       public:

              Handle();

              set();

              get();

              ......

              ~Handle();

       };

       #endif

 

       #include “handle.h”

       struct Handle::Cheshire //该结构存放所有Handleprivate成员

       {

              int val;

       };

       Handle()

       {

              simle = new Cheshire; //定义结构,分配内存

              simle->val = 0;

       }    

       ......

       ~Handl()

       {

              delete simle;  //释放

       }

10、函数重载:参数列表不同,函数名相同的多个函数。

       函数重载的实现:函数的内部名附带上参数类型,如

       void print(char c);

       void print(float f); 编译成:_print_char_print_float的内部名

11、在C++structclass唯一的不同之处就在于,struct默认为publicclass默认为private

12、默认参数的规则:

       a、只有参数列表的后部参数才可以是默认的,不可以在一个默认参数后面又跟一个非默认的参数;

       b、一旦在一个函数调用中开始使用默认参数,那么这个参数后面的所有参数都必须为默认的,这可

              从第一条中推出;

       c、默认参数只能放在函数声明中,通常在一个头文件中。

13、标准C具有字符串化运算符:#。用在预处理器宏定义中,可获得任何一个表达式并把它转换成一个字符数组。如:#define P(EX)    printf("%s : %d\n", #EX, EX)

14C++const默认为内部连接,也就是说,const仅在const被定义过的文件里才是可见的,而在连接时不能被其他编译单元看到。当定义一个const时,必须赋一个值给它,除非用extern作出了清楚地说明:extern const int bufsize; 通常C++编译器并不为const创建存储空间,它把这个定义保存在他的符号表里,但上面的extern强制进行了存储空间的分配(P176),另外还有其他一些情况,如取一个const的地址,或是一个编译期间不知道初始值的const,或为const集合,也要进行存储空间分配。例如:

       const int i = 100;

       const int j = i + 10;

       long addr = (long)&j;  //需获取const地址,需分配存储空间

       char buf[j+10];

       const char c = cin.get();   //编译期间初始值不知道,需分配存储空间

       const char c2 = c + ‘a’;

15、若为const集合,则编译期间不能使用它的值,如:

       const int i[] = {1, 2, 3, 4};

       float f[i[2]];  //错误,为const集合,其分配的存储空间内的内容,编译期间并不需要知道,P177

16CC++const的区别(P178)

       aCconst默认为外部链接,C++中默认为内部连接;

bC中一个const总是需要创建一块内存空间,C++中根据使用情况而定,若仅用于用一个名字替代一个值(#define用法),则不  分配存储空间。

17、指针常量: Type * const pointer ;    //必须把const表明放在*右边才是一个const指针

       常量指针: const Type *pointer ; Type const* pointer

       不存在const* char它无法通过编译

       从右向左读(* 读成 pointer to)

       char * const p;        //读作p is a const pointer to char

       const char * p;        //读作 p is a pointer to const char

       char const * p;       // const char * p;读法一样)

18char* str = "hello"; hello被编译器作为一个常量字符数组建立,str是指向常量字符数组首地址的指针,但是,修改该字符数组的任何字符都会导致运行时错误(并不是所有的编译器都会做到这一点),好的风格是这么定义:char str[] = "hello";

19、有一种观点:C中的一切都是按值来传递的,传递指针时,也会得到一份副本,即也是通过值传递指针的。

20、临时量:临时量自动生成为常量,改变临时量是错误的,因为编译器使所有临时量自动生成为const的。临时量常产生于函数返回值等。

21、一个内部类型的static const可以看作一个编译期间的常量,同时,必须在static const定义的地方初始化,而类中所有其他数据成员必须在构造函数或其他成员函数里初始化。如:

       class  Example

       {

              private

                     static const int size = 100;  //必须在定义时初始化

                     const string * stack[size];

              .............

       }

22const对象不能调用非const成员函数;const成员函数调用const、非const对象和非const数据成员都是安全的;非const对象调用const和非const成员函数是正确的。

23const实现有按位const和按逻辑const

       a按位const意思是对象中的每个字节都是固定的,对象的每个位映象都从不改变;

       b按逻辑const意思是整个对象从概念上讲是不变的,但是可以以成员为单位改变。

       编译器默认是将const对象编译成按位const,保证其常量性。

24、实现按逻辑const的方法:

       1、关键字mutable,指定一个特定的数据成员可以在一个const对象里被改变,如:

       class Z

       {

              private:

                     int j;

                     mutable int i;

              public

                     void f() const

                     {

                            j++; //错误

                            i++; //正确,有mutable标识

                     }

       }

       2、此功能实现的另一种方法是将this指针强制转换成当前对象类型的指针,再进行操作:

       ((Z*)this)->i++; (const_cast(this))->i++;

25、内联函数:必须使函数体和声明结合在一起才有效,即inline关键字必须和函数定义在一起。

       任何在类内部定义的函数自动成为内联函数。

26、一般任何类型的循环都会被认为太复杂而不扩展为内联函数。

27、向前引用:

       class Forwar

       {

              private:

                     int i;

              public:

                     int f() const {  return g()+1; }   /*在此之前g()未定义,但C++语言规定:只有在类声明结束后,其中的内联函数才被计算因此这是可以的。*/

                     int g() const { return i;   }        

       };

28、标志粘贴:##。它允许设两个标识符,并把他们粘贴在一起自动产生一个新的标示符。例如:

       #define FILED(s)     char* s##_string;              int s##_size

29、在CC++中,static都有两种基本的定义:

a 在固定的地址上进行存储分配,也就是说对象是在一个特殊的静态数据区上创建的,而不是每次函数调用时在堆栈上产生的,也就是静态存储的概念。

b 对一个特定的编译单位来说是局部的,这样static控制名字的可见性,这个名字在本单元或类之外是不可见的。这也描述了连接的概念,它决定连接器将看到哪些名字。

30、所有的全局对象都默认是静态存储的,这些对象在程序进入main()之前,就已经被初始化了。

31、名字空间:

       anamespace只能在全局范围内定义,但他们之间可以相互嵌套;

       b、在namespace定义的结尾,右花括号的后面不必跟一个分号;

c、一个namespace可以再多个头文件中用一个标示符来定义,就好像重复定义一个类一样,但这里的意思是对同一名字空间追加名字。

       d、一个namespace可以用另一个名字来作它的别名:

              namespace BobsSuperDuperLibrary{

                     class widget{ .....}

                     ............

              }

              namespace Bob = BobsSuperDuperLibrary

       e、不能像类那样去创建一个名字空间的实例。

32、使用名字空间的三种方法:作用域解析(::);使用指令(using namespace xxx);使用声明(using namespace xxx)

33、类中的静态成员:

       a、静态数据成员定义必须出现在类的外部,而且只能定义一次;静态成员函数则可以在类内部定义。类中静态数组初始化方法:

              int CLASS::array[] = {x, y, z,.....};

       b、嵌套类(在另一个类内部定义)中可以有静态数据成员,而局部类(函数内部定义的类)中则不能有。

c、类中的静态成员函数只能访问静态数据成员,也只能调用其他的静态成员函数,因为静态成员函数没有this指针。

34、引用(&)就像能自动地被编译器间接引用的常量型指针。应用要点是任何引用必须和存储单元联系,访问引用时,就是在访问那个存储单元。它是该存储单元的别名,它不新创建副本,因此和被引用的对象共用内存。

       const int& q = 12; //正确      int x;  int& a = x; //正确

35、使用引用的原则:

       a、引用必须在创建时初始化;

       b、一旦一个引用被初始化为指向一个对象,它就不能改变为另一个对象的引用;

       c、不可能有NULL引用,必须确保引用时和一块合法的存储单元关联的。

36、常量引用:void f(int& i);   f(1);  //错误,1是常量,应改成:void f(const int& i);

       指针引用:void f(int*& p);

37、拷贝构造函数X(X&)(P251),解决对象按值传递时编译器对其进行按位拷贝的问题(C/C++参数传递默认都是按位拷贝实现的)。如:

       class X{}

       X func(X x)

       {

              .....

       }

       在调用函数func(func(x)),若无拷贝构造函数,编译器在拷贝x对象作为func的形参时,以按位拷贝的方式,此时,构造函数不会被调用,但析构函数会调用。若有拷贝构造函数,则会调用拷贝构造函数。

38、仅当准备用按值传递的方式传递类对象时,才需要拷贝构造函数,否则,不需要。类对象抑制按值传递的方法:声明一个私有的拷贝构造函数,甚至不必去定义它。这样如果用户试图用按值传递的方式传递或返回对象,编译器就会报错,因为拷贝构造函数是私有的,除非类的成员函数或友元函数以按值传递方式传递对象。       X(const X& x); //拷贝构造的函数头模型,注意参数需为引用

39、运算符重载:当运算符被重载为全局函数时:对于一元运算符是一个参数,二元是两个参数;当运算符被重载为成员函数时:一元运算符没有参数,二元运算符有一个参数,运算符左侧的那个对象为调用重载函数的实例对象。

40++--重载时,参数列表中含有int是后递增(),无int型参数则是前递增(),参数int是用来表示后缀的哑元常量。

41operator=operator[]只允许作为成员函数,若为全局的,那么会重定义内置的=[]等运算符。重载的总原则:检查自赋值,尤其是operator=

42、运算符重载中的参数和返回值:

       a、对于任何参数,如不会改变它,默认作为const引用传递,否则作为引用传递;

       b、返回值多为引用且为常量引用,赋值运算符的返回值应该是非常量引用。

43、返回值优化:return Integer(left.i + right.i);  若用 Integer tmp(left.i + right.i);,会发生三件事:a、创建tmp对象,包括构造函数的调用;b、拷贝构造函数把tmp拷贝到外部返回值存储单元里;ctmp作用域结束时调用析构函数。而用返回值优化,编译器则直接将对象创建在外部返回值存储单元,只调用一次构造函数。

44、不能重载的运算符:成员选择operator.、成员指针间接引用operator.*、没有求幂运算符**不存在用户自定义运算符;不能改变运算符优先级。

45MyType b;//调用构造函数

       MyType a =b; //调用拷贝构造函数,未定义的对象,必须调用构造函数初始化

       a = b;//调用a.operator=(b)

46、当类中含有指针时,必须定义的4个函数:普通构造函数,拷贝构造函数,operator=运算符重载,析构函数。

47、如果类包含对象或是从别的类继承来的,operator=会被递归调用。

48、实现自动类型转换的方式:

       a、构造函数转换:如果一个类的构造函数以另一个类型的对象(或引用)为单一参数,那么这个构造函数允许编译器执行自动类型转换。使用关键字explicit能抑制上述现象:explicit myClass2(myclass c&)。构造函数技术是目的类执行转换。

       2、运算符转换:创建一个成员函数:operator DesType (),不用指定返回值--返回类型就是正在重载的运算符名字。运算符转换是源类进行转换。

阅读(741) | 评论(0) | 转发(0) |
0

上一篇:没有了

下一篇:没有了

给主人留下些什么吧!~~