1.复制构造函数

构造函数除了默认的构造函数和有参数的构造函数外,还有一个特殊的构造函数-复制构造函数(也会默认生成一个拷贝构造函数)。当函数或者方法采用按值传递时,

编译器会将实际参数复制一份传递到被调用函数中,如果参数属于某一个类,编译器会调用该类的复制构造函数来复制实际参数到被调用函数。复制构造函数与类的其他构造函数类似,

以类名作为函数的名称,但是其参数只有一个,即该类的常量引用类型。因为复制构造函数的目的是为了复制实际参数,没有必要在复制构造函数中修改参数,因此参数定义为常量类型。

注意:只有函数或方法采用的是按值传递时,参数中有类对象,才会调用该类的复制构造函数。如果函数采用引用方式传递,则函数将不会调用复制构造函数。

总结:编写函数时,尽量采用按引用的方式传递参数,这样可以避免调用复制构造函数,能够极大的提高程序的执行效率。

2.类中的常量成员

 

 1 class CUser
 2 {
 3   private:
 4 const int m_nLevel = ;  //错误的定义,不能对成员变量直接进行初始化  
 5 }
 6 为了能够对常量成员进行初始化,可以在构造函数中对常量成员进行初始化。
 7 class CUser
 8 {
 9 private:
10 const int m_nLevel;
11 public:
12 CUser()    //默认构造函数
13 :m_nLevel(1)   //为数据常量成员进行初始化
14 {
15     ...
16 }
17     
18 }

 

在构造函数的初始化部分可以对数据成员进行初始化,但是不能通过构造函数的函数体为成龙成员赋值实现初始化操作。因此,如果在构造函数体中利用m_nLevel = 1;语句为常量成员赋值是错误的。

 

3.类的静态成员

类的静态成员能够被同一个类的所有对象共享,它可以作用域类上,通过类名访问。CUser::m_nCount

类的静态成员不能在类构造函数中进行初始化,可以在全局区域对类静态成员进行初始化。

int CUser::m_nCount = 10;   //在全局区域对类的静态成员进行初始化。

4.隐藏的this指针

在定义多个类对象时,每一个类对象都有自己的一份数据成员(静态成员除外).为了区别不同对象之间的数据成员,通过this指针,在每个类的成员函数(非静态成员函数)中都隐含包含了一个this指针,指向被调用对象的指针,

对于非const方法,this指针的格式为"类型*const this",在const方法中,this指针的格式为"const 类型 * const this".

 

5.内联成员函数

6.静态成员函数

静态成员函数与静态数据成员类似,它能够被类的所有对象共享,并且能够直接作用类上(通过类名直接调用静态成员函数)。静态成员函数也调用static 关键字定义。

7.类的重载成员函数

重载成员函数:

(1)函数名相同,参数类型或者参数个数不同

(2)在类中同名的const成员函数和非const成员函数可以作为重载成员函数同时存在,即它们的参数列表完全相同.

非const类对象调用非const成员函数     const类对象调用const成员函数.

 

8.类成员指针   

类成员指针是指能够指向类成员的一个"指针"(他不是一个真正的指针),例如,指向某一个类的成员函数或者类的数据成员。

(1)类成员函数指针

成员函数指针定义格式: 返回值类型 (类名::*函数指针名(参数列表));

例如类 class CUser;

bool (CUser::*funLogin)();   //定义成员函数指针

funLogin = CUser::Login;  //将funLogin指向CUser类中的Login方法.

 

1 bool (CUser::*funLogin)();  //定义成员函数指针
2 int main(int argc, char* argv[])
3 {
4     funLogin= CUser::Login;  //将funLogin指向    CUser类中的Login方法      
5 CUser user;
6 (user.*funLogin)();  //通过成员函数指针调用Login函数
7 return 0;
8 }

 

如果需要同时定义多个成员函数指针,可以先定义一个成员函数指针类型

typedef bool (CUser::*pLogin)();

pLogin funLogin, defLogin; // 定义多个成员函数指针

注意:虽然成员函数指针的用法与函数指针类似,但他不是一个真正的指针,因为它还要包含其他的一些信息,例如,它关联的成员函数是否是虚函数,如何查找虚方法表指针等,

通常,成员函数指针是作为包含这些信息的一个小的数据结构实现的。

(2)类数据成员指针

与类成员函数指针类型,也可以定义一个类数据成员指针,使得它能够指向类的数据成员,与定义普通指针类型,定义数据成员指针只是需要使用类名标识。

定义方法:  int CUser::* pLevel;    //定义数据成员指针

 成员数据指针指向类成员数据方法

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

{

  itn CUser::*pLevel;   //定义类数据成员指针

  pLevel = &CUser::m_nLevel;   //为数据成员指针赋值

  CUser user;

  user.m_nLevel = 1;

  cout << user.*pLevel << endl;  //输出值 为 1

  return 0;  

}

上例中如果m_nLevel是静态成员,那么pLevel = &CUser::m_nLevel; 表达式获得的是m_nLevel的真实地址,否则获得的就是m_nLevel成员在CUser类中的偏移量.

 

9.继承

继承是面向对象的最基本特征,它是实现多态的基础。继承能够根据已有类派生一个新类,称之为子类。子类默认继承父类中除了private类的成员之外的任何成员。

子类对象调用继承于父类的方法,子类的继承的方法会隐藏父类的方法。

如果在子类中访问父类中被隐藏的方法,需要指定具体的父类名。

如果在子类中隐藏了父类中的方法,那么父类中同名的方法(重载方法)都将被隐藏。如果需要访问父类中被隐藏的重载方法,同样需要指定具体的父类名。

10.虚函数与动态绑定

从父类派生一个子类后,可以定义一个父类的指针对象,然后通过子类的构造函数构建对象。

例如:CEmployee *pEmployee = new COperator();   //CEmployee 是 COperator的父类。 定义一个父类的指针对象,通过子类的构造函数构建

虽然利用了子类的构造函数构建父类对象,但是使用pEmployee指针调用的是父类对象的方法。因为pEmployee定义的类型是父类的CEmployee。编译器采用的是静态绑定原则。

动态绑定技术: 依据对象实际创建的类型来确定调用哪个类的方法,而不是依据对象定义时的类型来确定。动态绑定技术的实现主要是通过虚函数来实现的。在定义成员函数时,如果

使用virtual关键字,则该成员函数成为了一个虚函数。

父类中定义为虚方法,子类中重新定义该方法(函数名和参数列表相同)时,则该方法永远是虚方法,无论是否使用virtual关键字.此种情况就是不是方法的隐藏了,而是方法的改写或覆盖。

 

11.特殊情况下的析构函数的调用:

定义一个基类类型的指针,调用子类的构造函数为其构建对象,当对象释放时,先调用父类的析构函数还是先调用子类的析构函数,然后再调用父类的析构函数呢?

答案是如果析构函数是虚函数,则先调用子类的析构函数,然后再调用父类的析构函数;如果析构函数不是虚函数,则只调用父类的析构函数。可以想象,如果在子类中为某个数据成员在堆中分配了空间,父类中的析构函数不是虚方法,上述情况将使子类的析构函数不会被调用,其结果是对象不能被正确的释放,导致内存泄露的产生。因此,在编写类的析构函数时,析构函数通常是虚函数。

 

12.抽象类

一个包含纯虚方法的类被称为抽象类,抽象类时不能够实例化的,通常用于实现接口的定义。

在抽象类中也可以定义普通的数据成员和成员函数,但是不能够实例化抽象类。一个类无论有多少个方法,只要有一个方法是抽象方法(纯虚函数),那么这个类就是抽象类。

 

13.类型转换

类型转换是指将某一个类型的对象转换为另一个类型的对象。在应用程序中,对象类型的转换主要在子类和父类之间。因此转换主要有两种类型,即向上转换盒向下转换。

向上类型转换是指从子类转换到父类,这种转换是安全的,因为子类具有父类中被外界能够访问的方法。

向下类型转换是指从父类转换到子类,这种类型转换是不安全的。因为通常在子类中提供了父类不具有的方法,将父类转换为子类后,试图调用这些实际不存在的方法,必然出现错误。

父类CEmployee  子类COperator

向上类型转换

COperator *pOperator = new COperator();  //定义COperator对象指针

CEmployee *pEmp = pOperator;  //将子类对象指针直接赋值给基类指针   

向下类型转换

 

CEmployee *pEmp = new CEmployee ();  //定义CEmployee 对象指针

 

COperator *pOperator= pEmp ;  //错误的赋值语言   编译器不允许

为了实现强制类型转换,可以使用C语言提供的强制类型转换方法。

例如: 

 

CEmployee *pEmp = new CEmployee ();  //定义CEmployee 对象指针

 

COperator *pOperator= (COperator*)pEmp ;  //通过强制类型转换将父类转换成子类  但是会非常不安全 如果用pOperator调用COperator特有的方法,那么后果不可预料。

如果两类对象的之间不存在子类父类关系,使用强制转换,那么转换存在严重的隐患。

dynamic_cast动态转换,它是C++提供的一种类型转换方式,利用它可以实现两个对象的比较安全的转换。如果待转换的对象类型与目标类型不相同,则转换将会失败。

CEmployee *pEmp = new COperaotr();  //使用Coperator构造函数构建CEmployee对象   实际上pEmp是一个子类对象

COperator *pOperator = dynamic_cast<COperator*>(pEmp);  //进行类型转换

if(pOperator != NULL) //判断转换是否成功

注意:使用dynamic_cast关键字,必须启动编译器的运行时类型识别功能。

在程序中使用dynamic_cast关键字能够比较安全的进行向下类型转换,但是dynamic_cast有一个缺陷,就是待转换的源对象必须具有虚拟方法表,换句话说,源对象必须就有虚方法。

 

14.多继承

一个子类同时从多个类派生。

 

15.虚继承

基类CAnimal 

CBird继承 public  CAnimal   CFish继承public  CAnimal

CWaterBird继承 public  CBird和 public  CFish

在CWaterBird类中将存在两个CAnimal类的拷贝。能否在派生CWaterBird类时,使其只存在一个CAnimal拷贝呢?

为了解决该问题,提出了虚继承机制。虚继承使得子类只存在一个基类。

解决上面问题的虚继承方法:

基类CAnimal 

CBird继承virtual public CAnimal   CFish继承 virtual public CAnimal

CWaterBird继承public CBird和 public CFish  

这样CWaterBird类不在有两个CAnimal类的拷贝,而只存在一个拷贝了。

 

16.运算符重载之构造函数的自动转换与禁止

运算符重载可以实现两个对象的加,减,乘,除,赋值运算等。

构造函数的自动转换 

CUser  user = 2; 语句是不能通过编译的,不允许CUser类与整数进行赋值运算。为了使该语句合法,

需要在CUser类中定义一个特殊的构造函数,这样进行CUser类对象和整数赋值运算时将自动转换该函数。

需要在CUser类中自定义一个有参构造函数

CUser(int nData)

{

m_nLevel = nData;

}

这样CUser user = 2;语句与CUser user(2);作用相同了。

禁止构造函数的自动转换功能,可以在构造函数前使用explicit关键字.

explicit CUser(int nData)   //禁止构造函数自动转换

{

m_nLevel = nData;

}

此时 CUser user = 2;将无法通过编译,而CUser user(2);是完全合法的。

 

17.运算符重载

(1)运算符重载

(2)转换运算符

注意事项:

(1)并不是所有的C++运算符都可以重载。

对于C++中的大多数运算符来说都可以进行重载。但是,"::","?",":",".",运算符不能够被重载。

(2)运算符重载的基本准则

一元操作数可以是不带参数的成员函数,或者是带一个参数的非成员函数。

二元操作数可以是带一个参数的成员函数,或者是带两个参数的非成员函数。

“=”,“[]”,"->","()"运算符只能够定义为成员函数。

"->"运算符的返回值必须是指针类型或者能够使用"->"运算符类型的对象。

重载"++"和"--"运算符时,带一个int类型参数,表示后置运算,不带参数表示前置运算。

 

18.友元函数和友元类

对于类的私有数据成员和方法,只能在该类内部使用,在外界是不能够访问的。但是在开发应用程序时,确实遇到过需要在一个函数的内部访问类的私有数据成员和方法此种情况该如何解决呢?

友元函数提供了一种机制,使得在该函数中能够访问类的私有数据成员和方法。

把某个函数声明为某个类的友元函数,那么在这个函数中就可以方法这个类的私有变量或者方法。

友元类是在这个类的所有方法中都可以访问另一个类的私有数据成员和私有方法。

 

19.类域

类定义是,类成员的声明顺序是很重要的,先声明的成员不能够使用后声明的成员。但是有两个例外,第一种是内联函数和类的数据成员,内联函数对类的所有成员都可见。  

第二种情况出现在成员函数的默认参数。在定义类的成员函数时,可以为成员函数指定默认参数,这个默认参数不仅可以是一个常量值,还可以是类的数据 成员,准确的说应该是类的静态数据成员,类的非静态数据成员

不能够作为成员函数的默认参数。当定义类时,即使类中的静态数据成员出现在成员函数声明之后,它仍然可以作为成员函数的默认参数。

 

20.嵌套类

C++语言中,允许在一个类内部再定义一个类,这样的类称之为嵌套类。

对于外围类只能访问嵌套类的公有成员,不能访问嵌套类的私有成员。

对于内部的嵌套类来说,只允许其在外面的类域中使用,在其他类域或者作用域中是不可见的。

在设计嵌套类时,嵌套类的方法可以不是内联方法。可以将嵌套类中的方法放置在类的声明之外,但是需要在方法名之前加上外面类::嵌套类标示符。

例如:

void CList::CNode::OutputNode()

{

}

 

20.局部类

 

21.类模板

函数模板为函数的参数,返回值等提供了动态参数化的机制,使得用户能够动态设置函数的参数类型和返回值类型。而类模板能够为类的数据成员,成员函数的参数,返回值提供动态参数化的机制,

使得用户可以方便的设计出更为灵活的类。

函数模板 template <Typename Type>

类模板  template <class Type>

template <class Type, int nSize>

在应用程序中使用类模板时,还应该注意以下事项:

(1)模板参数与全局对象重名。

模板参数的名称在模板声明之后直接到模板介绍都可以在模板中使用。如果在全局区域定义了与模板参数相同的对象,则在模板中全局对象被隐藏。

(2)模板参数名不能与模板中自定义类型或类的名称相同。

 

22.在类模板中的静态数据成员

在类模板中用户可以定义静态的数据成员,只是类模板中的每个实例都有自己的静态数据成员,而不是所有的类模板实例共享静态数据成员。因为每个类都是不同的,类的静态成员也应该是每个类独立拥有的。但是对于同一类型的类模板实例,其静态成员是共享的。

 

23.异常处理

异常捕捉

抛出异常

try

{

  throw;

}

catch()

{

}

posted on 2014-07-30 11:21  言止予思  阅读(197)  评论(0)    收藏  举报