个人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、虚析构函数只能用于公有继承的类族中,这是它的局限。
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、虚析构函数只能用于公有继承的类族中,这是它的局限。
相关文章推荐
- C++查漏补缺之浮点数内存表示
- 查漏补缺——C/C++基本类型
- C++查漏补缺读书笔记二 static关键字
- C++查漏补缺——String(1)
- C++查漏补缺读书笔记三 指针与引用,动态绑定与静态绑定
- C++查漏补缺读书笔记一 友元(friend)
- C++查漏补缺之流状态
- 从零学习C++第十篇:查漏补缺
- C++ 的查漏补缺
- c++查漏补缺之自增自减运算符重载
- C++查漏补缺
- 查漏补缺(C++笔试)(更新中)
- C++查漏补缺——对象和类(一)
- 【C++注意事项】1 数据类型及类型转换
- C++ 继承 必须注意的地方
- 探讨:C++中函数返回引用的注意事项
- C/C++中需要注意的一些问题
- C++ PrimerPlus部分知识点总结(1-12)(个人总结,仅供参考)
- C/C++常用宏定义,注意事项,宏中#和##的用法
- 【C++注意事项】7 Library vector Type