C++基本特征、new的用法和虚函数

一、面向对象的四个基本特征


      面向对象的程序设计方法要求语言必须具备抽象、封装、继承和多态性这几个关键要素。
      面向对象程序设计,是通过为数据和代码建立分块的内存区域,以便提供对程序进行模块化的一种程序设计方法。对象是计算机内存中的一块区域,通过将内存分块,每个模块(即对象)在功能上相互之间保持相对独立。


       这些内存模块中不但存储数据,而且也存储代码,这对保证对象是受保护的这一点很重要,只有对象中的代码才可以访问存储于这个对象中的数据,这清楚地限定了对象所具有的功能(即一个对象在一个软件中所能起到的作用),并使对象保护它自己不受未知的外部其它的事件的影响,从而使自己的数据和功能不会因此遭到破坏。


      在面向对象的程序中,对象之间可以通过函数调用实现相互通信。一个对象可以调用另一个对象的函数,处于对象外部的代码就没有机会通过直接修改对象的内存区域。当对象的一个函数被调用时,对象执行其内部代码来响应这个调用,这使对象呈现出一定的行为。行为及其结果就是该对象的功能。对象被视为能做出动作的实体,动作在对象相互作用时被激发,换句话说,对象就像在宿主计算机上拥有数据和代码,并能相互通信的具有特定功能的一台较小的计算机。


  <1>抽象



       面向对象鼓励程序员以抽象的观点看待程序,即程序是由一组抽象的对象组成的。另一方面,我们又可以将一组对象的共同特征进一步抽象出来,从面形成“类”的概念。


       抽象是一种从一般的观点看待事物的方法,它要求程序员集中于事物的本质特征,而不是具体细节或具体实现。面向对象鼓励程序员以抽象的观点看待程序,也就是说程序是一组抽象的对象-类组成的(严格讲,C++程序不是纯面向对象的,因为程序中还有像main这样的全局函数)。程序从一组对象为起来,抽取公共的行为放入到一个类中,这是抽象分类的观点,不同类的对象具有不同的行为。


       类的概念来源于人们认识自然、认识社会的过程。在这一过程中,人们主要使用两种方法:由特殊到一般的归纳法和由一般到特殊的演绎法。在归纳的过程中,我们从一个个具体的事物中把共同的特征抽取出来,形成一个一般的概念,这就是“归类”;在演绎的过程中,我们又把同类的事物,根据不同的特征分成不同的小类,这又是“分类”。对于一个具体的类,它有许多具体的个体,我们就管这些个体叫做“对象”。


举个例子,“人”是一个类,具有“直立行走、会使用工具”等一些区别于其它事物的共同特征;而张三、李四、王五等一个个具体的人,就是“人”这个类的一个个“对象”。


      <2>封装


       所谓数据封装,就是将一组数据和与这组数据相关的操作集合组装在一起,形成一个能动的实体,也就是对象。在这种情况下,用户是不可以直接操作数据的,他必须通过和数据相关的操作来访问数据。换句话说,数据封装就是给数据提供了与外界联系的标准接口,无论是谁,只有通过这些接口,使用规范的方式,才能访问这些数据。同时,由于客户端总是和接口打交道,他也就不必要了解数据的具体细节。


         由此可见,封装要求一个对象应具备明确的功能,并具有接口以便和其它对象相互作用。同时,对象的内部实现(代码和数据)是受保护的,外界不能访问它们,只有局部于对象的代码才可以访问对象的内部数据。对象的内部数据结构的不可访问性称为数据隐藏。封装使得一个对象可以像一个部件一样用在各种程序中,而不用担心对象的功能受到影响。


       早期的软件设计方法,把数据和程序混在一起,结构化很差,被细称为“一碗面条”的编程方法。在这一阶段程序的可读性与可维护性都很差,于是产生了“软件危机”,为了解除这种危机便提出了结构化程序设计。在结构化程序设计里,虽然程序被分为不同的模块,以便大大减少不同模块之间的相互作用,但数据仍然属于整个程序的。这就又存在着这样一个问题:一方面,程序员在设计每一个模块的时候,都要或多或少地作全局考虑,模块与模块之间的耦合度相对太高了,势必增加不同模块的程序员之间沟通所带来的工作量;另一方面,在某地方对数据的改动,有可能又对整个程序产生难以预料的影响。随着软件工程的进一步发展,软件越来越大,数据越来越多,这个问题也越来越突出。


       数据封装的提出,就是为了解决这一问题。它一方面使得程序员在设计程序时可以专注于自己的对象,“各人自扫门前雪,莫管他人瓦上霜”,同时也切断了不同模块之间数据的非法使用,减少了出错的可能性。


        <2>继承


       所谓继承是指一个对象可以获得另一个对象的特性的机制,它支持层次类这一概念。例如:红苹果属于苹果类,而苹果类又属于水果类。通过继承,低层的类只需定义特定于它的特征,而共享高层类中的特征。


       <4>多态


       不同的对象可以调用相同名称的函数,并可导致完全不同的行为的现象称为多态性。利用多态性,程序中只需进行一般形式的函数调用,函数的实现细节留给接受函数调用的对象。这大大提高了我们解决复杂问题的能力。例如绘制三角形与绘制正方形所调用的绘制函数其效果肯定是不同的,但我们可以设计一个公共的Draw()函数代表绘制,而不同对象的绘制图形的具体细节则分别由具体对象负责实现。



二、new的三种用法


        new有三种用法:new operator、operator new、placement new


       new operator


new operator是最常见的用法,如 Emp* e1 = new Emp;(Emp是一个类) 这里new有两种作用:分配空间、初始化对象(调用了构造函数)


operator new


operator new作用是只分配空间,不调用构造函数,如:Emp* e2 = (Emp*)operator new(sizeof(Emp));


placement new


placement new作用是在已分配好的空间上初始化对象,不分配空间,调用拷贝构造函数,如:new ((void *)e2) Emp(*e1);


示例代码如下:


// Emp.h
#ifndef _EMP_H_
#define _EMP_H_
class Emp
{
public:
Emp();
Emp(const Emp& other);
~Emp();
};


#endif // _EMP_H_


// Emp.cpp


#include “Emp.h”
#include <iostream>
using namespace std;


Emp::Emp()
{
cout<<“Emp…”<<endl;
}


Emp::Emp(const Emp& other)
{
cout<<“Copy Emp…”<<endl;
}


Emp::~Emp()
{
cout<<“~Emp…”<<endl;
}


// main.cpp
#include “Emp.h”
#include <iostream>
void main()
{
using namespace std;
// new operator 分配空间,调用构造函数
Emp* e1 = new Emp;
// operator new 只分配空间
Emp* e2 = (Emp*)operator new(sizeof(Emp));
// placement new 不分配空间,调用拷贝构造函数
new ((void *)e2) Emp(*e1);
// 显示调用析构函数
e2->~Emp();
// 仅释放空间,不调用析构函数
operator delete(e2);
// 调用析构函数,释放空间
delete e1;
}


三、虚函数的用法

何时用虚析构函数?

class Base
{
public:
    Base();
    ~Base();
    …
};

class Derived : public Base
{
    …
};

Base *p = new Derived;
delete p;

这个例子中,基类指针p指向派生类对象,那么经由基类指针来释放该派生类对象,会导致资源泄漏。因为基类的析构函数不是虚的,delete p只会调用基类的析构函数,派生类的析造函数不会被调用。解决方法是将基类的析构函数改成虚析构函数。

      那么什么时候该用虚析构函数呢?任何一个类只要带有virtual函数,这意味着该类被设计为多态用途的基类,那么基本上这个类也应该有一个virtual析构函数。如果一个类不含virtual函数,通常表示它的设计意图是不被用作多态基类。当类不被当作基类或者不被用作多态性,令其析构函数为virtual是不好的做法。因为一个类有虚函数,类对象会增加一个虚表指针(virtual table pointer)来决定运行期间哪个虚函数该被调用。

     另外析构函数还可以是纯虚析构函数。具有纯虚函数的为表示抽象类,如果某个类没有任何一个成员函数,又想将它设计成抽象类,就只能将析构函数作为纯虚的。如:
class Test
{
public:
    virtual ~Test() = 0;
}

// 虚析构函数的定义
virtual ~Test()
{


}
      通常纯虚函数不需要提供定义,但是这边有个例外,纯虚析构函数必需给出实现体才能通过编译链接。析构函数析构函数析构的次序是最深层的派生类的析构函数先被调用,然后依次调用每一层的析构函数。所以编译器会在析构Test类的派生类的析构函数中创建一个对~Test()的调用动作,所以必须提供这个析构函数的定义。



你可能感兴趣的:(虚函数,new,cc++)