C++学习要点

来源:互联网 发布:学生开淘宝店 编辑:程序博客网 时间:2024/06/14 06:51
1.      传指针时,我们可以通过指针来修改它在外部所指向的内容。但如果要修改外部指针所指向的对象是不可能的。例如传递外部指针到函数内来分配空间,必须传递指针的指针或指针的引用。

2.      char carry[10] = {0}; 编译器会将其后所有的东西都置0;

3.      函数返回值为const时,返回的东西付给一个类型相同的标示后其不能为左值;

4.      const int *i; int const *i; int * const i; 前两个功能相同,说明I所指向的内容不变;最后一个说明指针指向的地址不变,但内容可变。

5.      类中的const成员函数。定义为在原型后加const。常量函数不能修改类中的任何属性。但有两种方法可以修改。

a)         {(myclass *)this->member1 = values;}

b)        将一个成员定义成mutable即可被常量函数修改。

6.      类中的常量const 类型的,不能在类中被用来定义数组。而enum {ONE=100; TWO=2};定义的ONE、TWO却可以。通常的enum定义的置分配问题:enum A{ L=9, Z};此时Z的值为10。

7.      用const定义的int可用来开辟数组,但const定义的常量数组中的元素,不能用来定义数组。

8.      用sizeof计算变量的空间,如果是数组,按实际空间返回;常量字符串(实际上是在静态内存区开辟的变量)sizeof返回比实际长度加一。如果是指针则不考虑它指向的空间大小,仅仅返回指针类型的大小。如果用sizeof计算函数的行参,即使是属组也仅仅返回一个相关类型指
针的大小。

9.      形如int iarray[] = {12, 124, 433};编译器会自动给iarray分配3个元素的长度。元素长度的个数计算公式为sizeof(iarray) / sizeof(*iarray)。

10.              拷贝构造函数:当行参和实参结合时,如果是复杂对象的传值类型,则调用拷贝构造函数生成一个临时对象作为实参,退出函数时,临时对象被调用析构函数释放。当返回值是复杂对象是,也是调用拷贝构造函数来赋值。这就出现构造函数和析构函数被调用次数不相
等的情况。拷贝构造函数的原型为A(A&),我们可在类中重载。(缺省的拷贝构造函数是使用位(bit)拷贝方法:浅层拷贝,不拷贝指针指向的内容)。

11.              volatile类型的变量告诉编译器,本变量不需要进行代码优化。在多线程的应用中,我们如果读入一个变量到寄存器,此时时间片到期,去处理其他线程了,在重新获得处理机时,volatile类型告诉处理机,重新从变量读取数据到寄存器,而不是用寄存器数据直接处
理,这样可以防止脏数据。

12.              class 和struct在一定程度上有相同的功能,只不过前者缺省的成员是私有的,后者在缺省时成员为共有的。故而class不是c++必需的保留字

13.              c和c++编译器,对相同的函数名编译后生成的相同的标示不同,故而在引用c的库文件时必须使用extern “C”告诉编译器,它是c的函数,按c的规则编译。通常我们使用的标准头文件已被处理过。

14.              #include “filename”; #include <filename>,前者先在当前目录下寻找文件,如果找不到再到系统规定的路径下找,后者直接到系统规定的路径下找。

15.              任何地方分配的静态变量(static),其生命周期和主进程相同。第二次定义一个已存在的static变量,对变量的内用无影响,但它的可见范围只在定义的范围内。(考研曾作错!)(从静态变量的特性不难理解,类中的static类型是所有对象共享的)

16.              内联函数(inline)在实现上实际和宏类似,在内联函数出现的地方将函数展开来避免函数调用时的出栈、如栈,提高效率。但内联函数的代价是:代码增大。inline函数适合成员函数和自由函数。在类中实现的函数自动为内联函数。inline必须定义到函数的实现上
,例如:inline int PlusOne(int) 是无效的。友元函数在类的体内被实现自动变为内联函数。

17.              #include <iostream.h>

#define DEBUG(X) cout<<#X"="<<X<<endl

其中的#X表示X被当作字符串输出。

18.              assert(0 != 0); 如果assert中的条件为假,则运行期间回退出程序,且报告出错代码的行号。(#include <assert.h>)

19.              静态对象在main结束或exit()被调用时才调用自身的析构函数。这意味着,在对象的析构函数中调用exit()是很危险的,有可能进入一个死循环中。调用abort()来退出函数,静态对象的析构函数并不会被调用。我们可以用atexit()来指定跳出main或调用exit时要执行
的操作,用atexit注册的函数,可以在所有对象的析构函数之前调用。

void exit_fn2(void)

{

    printf("Exit function #2 called\n");

}     //处理函数

atexit(exit_fn2);

20.              全局变量实际上用的是静态存储。静态变量的构造是在进入main之前调用的,在main结束时调用它的析构函数。变量的名字由小范围(c++而言):

//*.cpp

int a; //静态变量,但为 extern int a; 即它是全局的,外部可见的

static int b;       //静态变量,static 和extern相反,只在*.cpp中有效,对其他单元(文件)是不可见的。函数的定义和上面相同。

main()

{     }

类的静态成员变量可以如下赋值:int X::s=23;(在*.cpp中,无论公私都可以)

21.              名字空间(namespace): 定义一个名字空间,然后使用unsing就可以将当前的类型上下文转换名字空间所定地的.

namespace math

{

        enum sign{positive, negative};

        class integer{

        int i;

        sign s;

        public:

        interger(int I=0): i(i) {………}

        sign Sign() {………}

        …………………..   

        };//end class

interger A, B, C;

interger divide(interger, interger);

}//no ;



void q()

{

        using namespace math;

interger A; //hides math::A

A.Sign(negative);

Math::A.Sign(positive);

}

22.         一般对于函数flaot f(int a, int b); 某些c++编译器编译后生成_f_int_int的名字,有些c编译器则生成_f的名字。故在c++中链接c的库函数时要用extern “C”告诉编译器,按c的规则来编译函数。类似的还有extern “C”{#include “myhead.h”},c++还支持extern “C
++”{}.

23.         在函数调用时,传引用也是将指针压栈。

24.         构造函数、析构函数、赋值构造函数、重载的=,四者的调用顺序:(三种函数都已实现)

a)    X  x;     X  a=x;

result:

X:construct   

X:copy_struct

b)    X x;        X a;        a=x;

Result:

X:construct

X:construct

X:copy_stru

operator =

X:destruct

如果没有赋值构造函数则结果:

                     X:construct

X:construct

operator =

X:destruct

(如果直接X a=x;这不掉用一般的构造函数,调用复制构造函数)

    指向类的成员函数的指针:设 int X:: a(void){}

X x;

                  int (X:: *pf)(void)= &X::a;

                  (x.*pf)();

指向成员变量的指针: 设int i; 是X的成员变量

                     int X::*pm = &X::i;

                  X x;

                  x.*pm=12;

                  X *p=&x;

p->*pm=11;

25.         ++的操作符重载

const X& operator++() //++b;            const X operator++(int ) //b++

其中的第二个参数为哑元,永远也不使用到。

26.自动类型转换

a.)       class one{                                                  b} class two{

       public:        one(){}                                          public:       two(const one &){}

              };                                                               };

void f(two) {}

main(){ one ONE;       f(ONE);     }

此时会调用two中的一个构造函数进行类型的自动转换。但效率不高。可以阻止隐含的类型转换。方法如下:将类two给为

class two{ public:  explicit two(const one &){} };

explicit只对构造函数起作用。此时必须这样调用函数:f(two(ONE));

27.    一个理想的string类,它知道如何从string转换到char *:

       class string

{

       private:       char *s;

        public:

                string(const char *S="")

                {

                        s=new char[strlen(S)+1];

                        strcpy(s, S);

                }

                ~string(){delete s;}

                operator const char *() const {return s;}

};

      int main(void)

      {

        string str1("lizhihui2");

        string str2("lizhihui2");

        strcmp(str1, str2);

      }

28.    如果从一种类型到另一钟类型有多种转换方法,则会出错:

       classs Y;

       class X

{

              public:      operator Y() const; //convert X to Y

       };

       class Y{

              public:       Y(X) ;//convert X to Y

       };

       void f(Y);

       main()

{

       X x;

       f(x); //error: ambiguous conversion

}

29.删除数组对象:

       foo *fp = new foo[100];        delete []fp; 或 delete [100]fp;

       使指针更像数组:int *const q=new int[10];这样q不能移动则更像数组。

30.new堆内存用完时的异常处理器函数

void out_of_memory() {printf(“out of memory!\n”);        exit(1);}

main() { set_new_handler(out_of_memory); …………….}

31.new和delete的一种全局重载方法

void * operator new(size_t sz)

{

        printf("operator new :%d bytes\n",sz);

        void *m=malloc(sz);

        if(!m) puts("out of memory\n");

        return m;

}

void operator delete(void *m)

{

        puts("operator delete.\n");

        free(m);

}

class s

{

        int i[100];

public:

        s(){puts("s::S()");}

        ~s(){puts("s::~S()");}

};

int main(int argc, char* argv[])

{

        int *p=new int(23); //operator new :4 bytes

        delete p;                  //operator delete.



        s *pp=new s;           //operator new :400 bytes

        delete pp;                 //operator delete.



        s*pa=new s[3];       //operator new :1208 bytes, more 8 bytes for array info

        delete []pa;              // operator delete.       (C++Bilder unsupport)

        return 0;

}

缺省的系统new和delete是调用malloc和free来工作的,系统要维护一张内存分配表。分配出去的内存要记住它的大小和起始地址,释放时根据起使地址释放。

32.    重载new在特定的内存上分配空间

class s

{

        int i;

public:

        s(int ix=0){i=ix;}

        ~s(){puts("s::~S()");}

        void * operator new(size_t  d, void *loc) {return loc;}

};

int main(int argc, char* argv[])

{

        int len[10];

        s *ps = new (len+1) s(3412);  

//第一个函数相当于告诉new从哪里开始分配空间(隐含);

//它的值则是要分配的长度。特殊的分配要注意需特殊的释放。

        return 0;

}

new 在len的空间上分配空间给s对象。(new重载第一个参数必须为size_t系统会自动传给它一个大小尺寸)

33.    跳转函数setjmp、longjmp

void OZ()

{

        printf("there 's no placelike home\n");

        longjmp(kansas, 47);

}

jmp_buf kansas;

int main(int argc, char* argv[])

{

        if(setjmp(kansas)==0)  OZ();

        else       printf(" I have a dream..\n");

        return 0;

}//执行完OZ()后,会立即跳转到printf(" I have a dream..\n");执行

setjmp()是一个特别的函数,当被调用的时候,它吧当前的进程状态的相关信息放到buff中,并返回0;如果使用longjmp对同一个buff操作,这就像再次从setjmp中返回,即正确弹出setjmp的后端。这时返回值对于longjmp是第二个参数,所以能发现实际上从longjum中返回了。

34.    异常定制和抛出

class up{};

class fit{};

void g();

void f(int i) throw (up, fit)

{

        switch(i)

        {

                case 1: throw up();

                case 2: throw fit();

        }

        g();

}

void g(){throw 47;}

void my_unexpected()

{

        printf("unexpected handle!\n");

        exit(1);

}

int main(int argc, char* argv[])

{

        set_unexpected(my_unexpected);

        for(int i=1;i<=3;i++)

        {

                try{ f(i);}

                catch(up)  {printf("catch up\n");}

               


                catch(fit){printf("catch fit\n");}

        }

        return 0;

}

set_unexpected设置处理系统不认识的异常情况(缺省是中断)(异常处理安装器缺省指向terminate())。上面我们定义了up、fit两种异常抛出类,并抛出了这两种异常,来捕获。抛出异常时也生成了异常的一个对象。Catch(…){}捕获所有异常。( 但失去了截获的异常类型)

35.    当有未被捕获的的异常时,系统缺省调用terminate(),它调用abort()函数直接从进程中退出,此时静态全局变量的析构函数未被调用。可以使用set_terminate来安装自己的terminate函数,用法和上面的几个安装起一样。他返回的typedef void (*terminate_handler)();为老
的处理器指针。

        当一个构造函数在分配资源时,如果这时有unexpect异常到达,系统会结束而不会调用析构函数来释放已 分配的堆内存。

36.    运行期间的类型判定(run-time type identification, RTTI)

a.) 编译器实现。

使用函数typeid(objname).name()就可得到函数的名字。实际上typeid()返回全局typeinfo类的常量对象的一个引用。使用before来判断一个对象是否在另一个对象前定义。

fit ft;

        up u;

        if(typeid(ft).before(typeid(u))) printf("lzh\n"); //is true

b). 安全方法向下映射法

         C* pc = dynamic_cast<C*>(pd);   // ok: C is a direct base class
                                          // pc points to C subobject of pd

                                          // pc points to C subobject of pd
            判断pd时不是一个C*类型的对象,如是则返回一个指针,否则返回NULL.是通过试图指派法来断定的,与第一种方法不同。

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 永辉超市积分卡怎么办 超市积分卡丢了怎么办 医保卡磁条坏了怎么办 社保卡磁条坏了怎么办 鞋子长了怎么办m.s.cn 厚底皮拖鞋穿松了怎么办 白色帆布鞋洗后发黄怎么办 运动鞋子买大了怎么办 格力空调出现fo怎么办 绝味鸭脖代金券的附券撕了怎么办 耐克鞋子开胶了怎么办 苹果6s自动重启怎么办 钱不够想买手机怎么办 安卓机屏幕密码忘了怎么办 屏幕解锁密码忘了怎么办 华为手机屏幕解锁密码忘了怎么办 oppo锁屏密码忘了怎么办 云助理密码忘了怎么办 购买方发票丢了怎么办 普票发票联丢失怎么办 唯品金融没还款怎么办 金点原子锁打不开了怎么办 87彩店注册不了怎么办 微店如果不退款怎么办 微信上微商被骗怎么办 微商代理不做了怎么办 微店拒收不退款怎么办 在微商买东西被骗怎么办 云集买家买东西不退款怎么办 微信红包密码忘记了怎么办 微信购物不退货怎么办 微信隐私设置无法添加怎么办 微信支付被限额怎么办 微信发现没有购物怎么办 微信转账钱被骗怎么办 玩连环夺宝输了好多钱怎么办 厘米秀换不了装怎么办 社保只缴纳两年怎么办 502盖子粘到手上怎么办 口红粘在盖子上怎么办 玫瑰手杖永久错过了怎么办