Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1689217
  • 博文数量: 76
  • 博客积分: 2175
  • 博客等级: 大尉
  • 技术积分: 2481
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-20 20:49
个人简介

欢迎光临我的博客

文章分类

全部博文(76)

文章存档

2018年(4)

2017年(1)

2016年(2)

2015年(2)

2013年(5)

2012年(29)

2010年(33)

分类: C/C++

2010-04-22 21:44:52

本人从事C++开发多年,下面是我个人的一些心得体会,写出来与大家共享、探讨。
 
C++比较 C
1.继承和多态

class A
{
public:
    virtual void init();
    void Do()
    {
        printf(m_str);
    }
  
    void Handle();
private:
     char m_str[100];
}
void A::init()
{
    strcpy(m_str, “AAA”);
}

void A::Handle()
{
    init();
    Do();
}


 
一个非常简单的例子,现在A::Handle()的功能就是调用init初始化成员m_str
AAA,再调用Do将其打印。
调用者只需如下调用:
 

A a;
a.Handle();


 
如果现在有需求需要初始化该字符串为BBB,再将它打印,那么只需从A派生出
B,重载实现init即可:

class B : public A
{
     virtual void init();
};

void B::init()
{
     strcpy(m_str, “BBB”);
}

如下调用:

B b
b.Handle();


在这里,只修改了一个函数init。如果使用C来实现情况会怎么样呢?

实现类A的代码是:

char m_str[100];
void init()
{
     strcpy(m_str, “AAA”);
}
void Do()
{
     printf(m_str);
}

void Handle()
{
    init();
    Do();
}

如果这时同样实现上述初始化m_str为 “BBB”的功能,而需要保留原实现,则首先需要增加函数init_BBB()如下:


void init_BBB()
{
    strcpy(m_str, “BBB”);
}

同时需要增加Handle_BBB()如下:
void Handle_BBB()
{
    init_BBB();
    Do();
}


 
使用C++只需要派生新类,重载实现虚函数init()即可,而使用C语言需要同时增加两个函数。为什么呢?因为在C++的实现中,因为类AHandle()已经调用了虚函数init(),所以尽管它本身不是虚函数,但它的行为也是多态的。而使用C实现时,函数均为静态,从而如果需要增加新的功能,如果是影响了该模块的内部函数实现,那么需要增加所有相关的外围实现(调用者)。当然,C语言中也可以采用函数指针、回调函数等方式来实现模块间的动态调用,但由于C语言本身的机制和C++不一样,所以不能做到像C++那样。
而为什么限制不可以修改原有代码,因为它可能是第三方提供的一个类库或者函数库,我们根本没有源代码的实现。即便是我们自己内部的实现,对已经稳定的源代码的改动也不是很好的事情。因为最好重用而不是修改已有的实现。
 
这个例子有些简单,使用C来实现同样的新功能并没有比C++增加多大的工作量。但随着功能需求的复杂,使用C来实现新增的工作量可能会大得多。

我在以前某项目中就曾经遇到过。

项目在version2.0时,需要增加对用户发出的SIP消息进行数字认证(Digest Authentication)的功能。当时采用类CDigestAuth来实现:

 
 

class CDigestAuth

{

public:

virtual string GetNonce();

void Handle ()

{

     string strSystemNonce = GetNonce();

     …



}

};


CDigestAuth *auth = new CDigestAuth(…);

auth->Handle();


而在version 3.1时,system nonce除了存储在DB中,还会存储在Cache中,而程序需要首先从Cache中查询,如果找不到,再从DB中查找,这样GetNonce函数就需要重写。当时写了一个新类CMemDigestAuthCDigestAuth继承:




class CMemDigestAuth : public CDigestAuth
{
   virtual string GetNonce();
}
 
string CMemDigestAuth::GetNonce()
{
    memory query to get the system nonce and return;
    if not found, call CDigestAuth::GetNonce();
}


CDigestAuth *auth = new CMemDigestAuth(…);

auth->Handle();


1.1.2 分析

为什么C语言和C++会有这样的差别呢?原因就在于:

1.       C面向数据设计方式,采用模块化设计思想,在定义好模块间的接口后,更多地着眼于单个功能模块上的实现,而单个功能的实现是静态的。这样系统实现是各个功能模块的静态组装叠加。从而已有功能在被重用时,也是静态的叠加或重组。而用C++实现时,把行为(方法)和属性(数据)进行封装、抽象为单独的对象。使用者只能通过对象所提供的方法对它进行操作,屏蔽了内部实现细节。使用者拥有对象,就获得了该对象具备的所有功能。这不仅增加了安全性,而且对象的组合、包含使得功能多样化。

 

2. 继承使得子类获得了父类几乎所有的功能,而它又可以增加自己的行为和属性,或者修改父类的行为或属性。这样,只要少许的代码改动就可以创造出一个新的对象,从而实现新的功能,而且对于使用者而言,它所需要的改动很少,甚至没有。

 

3. 多态是继承的一个重要目的,尽管不是全部。子类可以重写虚函数而改变父类的行为,而使用者可通过父类对象就可以访问子类。如果没有多态,那么调用者就需要定义每一个具体的派生类对象的指针来调用它们,有了多态,它只需要记录父类指针来使用,但同样的代码可呈现出不同的功能,这就是多态。

 

4.       在对象和对象之间建立联系后,功能将变得更加丰富。比如,一个对象可以调用另外一个对象的方法,或者它的成员中可以包含另一个对象。这样多个对象之间的继承、包含、组合、参数传递等就构成了一个个面向对象的设计模型,它可以解决一系列问题,

 

现在已有很多成熟的设计模型,比如Composite(重合)、Bridge(桥接)等。一个好的设计模型着眼于把需要实现的功能抽象成多个有着继承、包含、组合等关系的对象,这样系统中所有的行为方式都是面向对象的行为方式。

对于相同的事物,不同的人可以抽象出的不同的对象。但好的设计模型可以巧妙地抓住事物内一类或一系列对象内在的逻辑联系和规律,并以面向对象的方式加以表达。

一个好的设计模型不仅可以实现已有功能,还具有良好的扩展性和复用性。或者说,好的设计模型都有一个好的框架。

 

当然,使用C++并不是没有代价的,最大的问题就在于它的性能上(对象的拷贝、构造/析构函数的使用等);其次面向对象的设计对开发者有更高的要求,因为面向对象的思维方式更难一些。最后,并不是所有的问题都适用于面向对象解决,有些使用面向过程更适合。




《返璞归真--UNIX技术内幕》在全国各大书店及网城均有销售:
亚马逊                          China pub
上学吧                          1号店

阅读(10768) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~