个人C++注意点(查漏补缺)(2008-10-25):

个人C++注意点(查漏补缺)(2008-10-25):

1A[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;
  //
常量区 —常量字符串就是放在这里的。 程序结束后由系统释放
 解决办法四:
  堆空间

7int a[4]={1};
 

8、声明指向成员的指针

 声明指向公有数据成员的指针
类型说明符(类名::*指针名)

 声明指向公有函数成员的指针
类型说明符(类名::*指针名)(参数表)

 对指向数据成员的指针赋值:
  指针名=&类名::数据成员名;


 通过对象名(或对象指针)与成员指针联
 – 手来访问数据成员
  对象名.* 类成员指针名
   或:
  对象指针名—>*类成员指针名

 指向函数成员的指针
赋值

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

– 通过对象名(或对象指针)与成员指针
结合来访问函数成员
(
对象名.* 类成员指针名)(参数表)
或:

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

 

9Point &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的值不会被计算,而用函数实现重载时会改变它这种特性)

24Point 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是真是假 bc都会被转换为同一种类型


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

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

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

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

34、虚函数的实现机制
  编译器发现某类含有虚函数,则对其生成的对象悄悄地加入一个void 型的指向指针的指针:vptr
 并让其指向一个由类维护的虚函数表vtable(其实是个指针数组),每个表项是一个虚函数名(地址)
 排列次序按虚函数声明的次序排列。
  在类族中,无论是基类还是派生类,都拥有各自的vptrvtable。相同类型所生成的对象共享了同
 数一个vtable
  该项技术的实质是“将找谁变成到哪去找”——不用管找到的是哪一个。

 

35friend函数和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
、虚析构函数只能用于公有继承的类族中,这是它的局限。

 

 

 

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值