您的位置:首页 > 编程语言 > C语言/C++

个人C++注意点(查漏补缺)

2013-12-31 21:54 309 查看
个人C++注意点(查漏补缺)(2008-10-25):

1、A[0]

它是一个表达式,而不是一个名字:(A+0)

2、通过初始化列表赋初值:

int a[2]={int(1),int(2)};

3、前置声明只能用于引用和指针,不能用于对象

4、构造函数天然具有类型转换功能,除非用explicit关闭

5、指针的引用

int *p;

int *&q=p;

但引用不能再有引用。

6、针对函数返回指针一定要特加注意;

解决办法一:

static char p[] = "hello word";

return p;

静态变量是在全局变量区域的

全局区(静态区)(static)—,全局变量和静态变量的存储是放在一块的,

初始化的全局变量和静态变量在一块区域,

未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。

- 程序结束后有系统释放

解决办法二:

全局变量的方式

解决办法三:

const char *p="hello word";

return p;

//常量区 —常量字符串就是放在这里的。 程序结束后由系统释放

解决办法四:

堆空间

7、int a[4]={1};

8、声明指向成员的指针

声明指向公有数据成员的指针

类型说明符(类名::*指针名);

声明指向公有函数成员的指针

类型说明符(类名::*指针名)(参数表);

对指向数据成员的指针赋值:

指针名=&类名::数据成员名;

通过对象名(或对象指针)与成员指针联手来访问数据成员

对象名.* 类成员指针名

或:

对象指针名—>*类成员指针名

指向函数成员的指针赋值

– 指针名=类名::函数成员名;

通过对象名(或对象指针)与成员指针结合来访问函数成员

(对象名.* 类成员指针名)(参数表)

或:

(对象指针名—>*类成员指针名)(参数表)

9、Point &r = *(new Point(2,4));

delete &r;

10、类的聚集中的浅拷贝,深拷贝(改赋值函数和拷贝构造函数)。

(对象计数器的使用)

赋值运算函数应该考虑:是否为自赋值;



11、私有继承的时候

可以用using 类型名::的语法“打捞”出来----回归到原来的级别

注意: 1、不能区分重载,有重载的全部“打捞“出来

2、只能还原,不能提高访问级别

3、只能还原直接基类,不能“隔代”打捞

4、不能让子孙类重复“打捞”

12、必须用初始化列表的地方

1、继承

2、组合

3、引用

4、常数据成员



13、初始化列表真正执行的顺序服从定义列表的顺序或继承列表的顺序

14、类型兼容千万别用在对象数组上

1、用指针数组,而对象不放在数组中

2、改用强类型的vector vector<B *> vbp;

15、静态常数据成员相当于程序中的”常量“

16、静态数据成员属于类属性,而常数据成员属于对象属性

17、虚拟继承--调整指针--对象体积膨胀

18、不要用虚拟继承来分辨同名二义

private继承来的和由public继承的两个同名函数,不能想当然的认为private继承来的不能访问而public继承来的可以访问

就不造成二义

19、注意对齐

class base

{

char x;

};

class A:virtual public base

{

int a1;

};

class B:virtual public base

{

int b1;

};

class C:public A,public B

{

int c1;

};

void main()

{

cout<<"sizeof(base):"<<sizeof(base)<<endl; //1

cout<<"sizeof(A):"<<sizeof(A)<<endl; //9

cout<<"sizeof(B):"<<sizeof(B)<<endl; //9

cout<<"sizeof(C):"<<sizeof(C)<<endl; //21

}

20、静态数据成员 static int i;

int A::i= 10; // 静态成员的初始化

21、内嵌类:class中有class的声明

局部类:函数中有class的声明

类的聚集:类的聚集描述的是一个类内嵌其他类的对象作为成员的情况,是包含与被包含的关系.

类的组合:用一个类的对象作为另一个类的成员的时候,就是类的组合



22、返回常对象的函数,目的是防止返回值被作为左值赋值。 (a+b=c;)

23、"&&"," ||" 不能用于重载 (注意其特点 a&&b ,当a为假的时候b的值不会被计算,而用函数实现重载时会改变它这种特性)

24、Point a,b;

Point c = a + b; //隐含调用 operator+ ,此时还会调用拷贝构造函数

Point c;

c = a + b ; //此时会调用赋值函数operator= 和 operator+

显示调用为: c.operator=((a.operator+(b));

运算符成员函数的设计:

25、前置单目运算符重载函数Clock& Clock::operator ++() 必须返回一个引用型

++(++a);

若返回一个对象而非引用,返回时该对象会拷贝到一个临时变量上,变成了这样:++(temp); 这样真正的a就只自加了一次

26、后置单目运算符重载Clock Clock::operator ++(int) 必须返回一个对象

(a++).setTimer(); 千万不要试图这样来修改一个对象的值,因为(a++)返回的只是一个副本

//后置单目运算符重载

Clock Clock::operator ++(int)

{ //注意形参表中的整型参数

Clock old=*this;

++(*this); //调用了另一个成员函数

return old;

}

27、友元函数的Name和成员函数的Name一定不能相同

28、 运算符 使用形式 等价式

双目运算符@ oprd1 @ oprd2 operator @(oprd1,oprd2 )

前置单目@ @oprd operator @( oprd )

后置单目@ oprd @ operator @( oprd , 0 )

类型转换

29、三目运算符 a ? b : c

不管a是真是假 b和c都会被转换为同一种类型

30、 构造函数放在private中,可以防止类型转换时的"外转内"

31、类型转换函数可以被继承

32、虚函数具有继承性。基类中声明了虚函数,派生类中无论是否说明,同原型的函数都自动为虚函数。

33、虚函数只能用于非静态非友元非内联的成员函数。虽然不可以是友元函数,但可以外派成为另一个类的友元函数。

34、虚函数的实现机制

编译器发现某类含有虚函数,则对其生成的对象悄悄地加入一个void 型的指向指针的指针:vptr,

并让其指向一个由类维护的虚函数表vtable(其实是个指针数组),每个表项是一个虚函数名(地址),

排列次序按虚函数声明的次序排列。

在类族中,无论是基类还是派生类,都拥有各自的vptr和vtable。相同类型所生成的对象共享了同

数一个vtable。

该项技术的实质是“将找谁变成到哪去找”——不用管找到的是哪一个。

35、friend函数和static函数皆可inline

36.有虚基类时构造函数的调用次序

1、无论虚基类与产生对象的派生类相隔多远,首先调用虚基类的构造函数;

2、然后按继承次序调用直接基类的构造函数

3、如果有包含的对象,再按声明次序调用所包含对象类的构造函数;

4、最后才是执行函数体(普通类型数据成员被赋予初值)。

37、在多继承的情况下,如果子类没有将父类们的同名函数都覆盖,无论是否使用虚继承,都将发生“访问模糊”错误。

38、:: 的用法小结:

1、用于将类内声明的静态数据成员在类外初始化;

2、用于将类内声明的函数成员在类外实现;

3、用于捞出继承时访问权限被改变的成员,使之还原

为原来的权限; 为原来的权限;

4、继承时派生类新增了与基类同名的成员,由于同名

屏蔽,从而使基类的成员被遮蔽,可用: :将被遮蔽

的基类成员重见天日;

5、用于在类外或子类中访问不继承的静态成员;

6、用于区分不同名空间的标识符。

39、: 的作用之一:

恢复访问权

class A

{

public:

void get_XY();

void put XY();

void put_XY();

protected:

int X,Y;

};

class B:private A //私有继承 p

私有继承

{

public:

using A:: get_XY;

void make S();

void make_S();

private:

int S;

};

• 恢复时不可带类型名;( void A::get XY(); )

• 只能恢复不可提升或降低访问权限;

• 当父类被恢复的函数重载时,将全都恢复;

• 父类中不同访问域的重载函数不能被恢复;

• 若子类新增了与父类同名且同形参表的成员,则父

类中的该成员不可恢复,否则会造成重复定义。

40、

int a=10;

&(&a)是不允许的。&a是一个表达式,对表达式取地址会得到一个错误的结果。

41、

字符数组的初始化

对数组初始化时,若指定元素个数,则编译器会自动加尾/0,否则不加。但若采用双引号初始化,则会加 。

char a1[10]={'a','b','c','d'};

char a2[ ] = {'x','y','z'};

char a3[ ] = "sdfg";

printf( %s/n ,a1);

printf("%s/n",a2);

printf("%s/n" a3);

运行结果:

abcd

xyz乱码

sdfg

42

类型转换

1、 转换函数格式: operator 类型名();

1. 该函数不得写返回类型;

2 该函数不得写参数;

3. 其中的类型名可以是 int char...各种类型,还可以是:

*

类型名 []const

()volatile

4. 函数体内定要有 return 语句;

5. 可以被继承;

6. 可以是虚函数;

7. 一个类可以有多个转换函数。

8. 该函数应是类的非静态公有成员,为常函数更佳;注意此时的const应该放在….。它与类型名中的const 无关。

43

虚函数的弊端

多层继承后,若无虚函数,尽管用指针指向各类对象,但仍不能调用子类的同名函数。

44

虚析构函数

1、为何需要虚析构函数?

避免析构对象不彻底。

2、何时需要虚析构函数?

当一个类含有虚函数时。

因为,当你打算用基类指针(或引用)删除子类对象时,由于切割现象,会只释放基类成分(部分

销毁),这样会遗留内存垃圾。而且会因遗漏了子类的析构函数调用,导致没有释放子类把持的资源。让基类的析构函数成为虚函数,则会调用到子类的析构函数,这样就会彻底释放内存和资源。

3、

当基类的析构函数为virtual时 子类的析构函数自然为virtual。

反之,当基类的析构函数为非virtual,而子类的析构函数为virtual时,子类对象不具有多态虚性。即,释放对象时会遗留内存垃圾

4、

当程序员没有显式给出子类析构函数时,系统自动产生的默认析构函数也为virtual的。

5、虚析构函数只能用于公有继承的类族中,这是它的局限。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: