首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > C++ >

被误解的C++——高端开发解决办法

2012-02-04 
被误解的C++——高端开发诸位留神,我要捅马蜂窝了。呵呵,玩笑。之所以这么说,因为我打算在这里探讨如何利用C++

被误解的C++——高端开发
诸位留神,我要捅马蜂窝了。
呵呵,玩笑。之所以这么说,因为我打算在这里探讨如何利用C++的特性优化高端编程。高端的编程历来是VB、Delphi、Java、C#等等语言的一亩三分地,如果我敢在这里说个不字,那点口水也能把我淹死。不过,为了C++,豁出去了,就让我一个人挨炸弹吧。
我的案例是一个小小的,不起眼的界面问题。尽管是小小的,不起眼的问题,但它的解决为我们指出在高端编程优化的一种途径。这种构想是否可行,以及是否值得,我不清楚。因为所需的机制,还在标准委员会的讨论之中,我还无法制作完整的案例加以检验。
一切从一个ComobBox开始。
有一次,我从数据库提出一组数据,放入一个ComboBox。ComboBox的每个项目还必须和一个值绑定,这样当选中一个项目的时候,便可以得到一个id,用于数据操作。这是再常规不过的操作了。在Windows   API,MFC等库中,可以为每个项目指定一个“Item   Data”或者“Item   Data   Pointer”。前者是个long,对应类型为long或能够转化成long的数据;后者是void*,对应那些非整数类型的数据。
在.net中,技术发展了,利用了OOP的多态。ComboBox的Item是一个Object^类型,我们可以创建一个ref   class,重定义ToString()方法,而这个类中依然可以保留其他数据。.net中任何类型都可以多态地赋给Object^类型。ComboBox则调用每个Item的Object::ToString()方法,获得所需的显示内容。于是,ComboBox的Item既可以提供ComboBox所需的输出,也解决了附加数据的问题:
class   MyItem
{
public:
MyItem(int   id,   String^   name)   :   _id(id),   _name(name)   {}
public:
int_id;
String^_name;

String^   ToString()   {
return   name;
}
};
combobox1.Add(gcnew   MyItem(10,   “abc”));
这些操作我在以前已经做过无数次了。但这次不同。我不小心踩了个地雷。这次不同于以前,id不再是long,而是String^。但我还以为是long,所以提取出这个id的时候,出了大错:
MyItem^   cur=dynamic_cast <MyItem^> (combobox1.SelectedItem);
cur-> …;   //运行时错误,cur==nullptr
幸好我用了dynamic_cast,不然还不知怎么样呢。因为放入ComboBox的对象不是MyItem类型,所以这个转换得到的是空句柄。于是,我的程序得到了了一个难看的运行时错误。
“唉,要是强类型多好!”我一声长叹。话音未落,一个念头瞬间闯入了我的脑海,就像漆黑的夜空,划过一道明亮的闪电,照亮了每一个角落。…
呵呵,没那么夸张。但我的确想到了“强类型”。
毋庸置疑,强类型可以为我们提供很多好处。首先,强类型可以减少类型错误,避免不必要的类型转换,确保类型安全;其次,由于ADT(抽象数据类型)的引入,使得类型在拥有数据结构的同时,也描述了外在的行为特征。因此,类型具备了描述业务逻辑的能力。于是,强类型化使得我们可以利用ADT的这些能力,将业务逻辑的约束直接映射到代码中。并且,利用类型的匹配机制,维持了这些约束,使我们得以在编译时拦截诸多与业务逻辑相关的错误;最后,强类型可以参与重载,使得我们可以用统一的形式编写具有相同逻辑含义的代码,以此优化软件开发。
当我想到强类型,立刻联想到模板。我们完全可以利用模板来解决这个问题。
我考虑了三种方案,(所有三种方案,我放在blog里,以节省篇幅),最终选择了一种组合方案:
首先,需要约定业务对象类(以管理员类Admin为例)提供一个默认的显示字符串构造函数,暂且称为DefDisp()。(通常,业务对象类都应该有一个默认的显示函数,为了便于在界面上显示)。然后,定义一个默认显示组织的函数对象:
template <typename   T>
struct   DefaultDispOp
{
string   operator()(const   T&   item)   {
returnitem.DefDisp();
}
};
最后,将DefaultDispOp作为XComboBox第二参数的默认参数:
template <typename   ItemT,   typename   DispOP=DefaultDispOp <ItemT>   >
class   XComboBox
{
...
string   GetItemDispString(const   ItemT&   item,   DispOp&   op)   {
returnop(item);
}
}
DefDisp()所对应的显示应该是该业务对象最常用的显示,比如对于Admin而言,可能就是全名。如果只需要用默认方式显示的,那么只需简单地用业务对象类实例化XComboBox即可:
XComboBox <Admin> combobox1;
XComboBox <Admin> combobox2;
而对于有特殊显示要求的,再另外提供转换函数对象。
struct   AdminDisp1
{
string   operator()(const   Admin&   admin)   {
…;
}
};
//用Admin和AdminDisp1实例化XComboBox
XComboBox <Admin,   AdminDisp1> combobox1;
三种方案的分析可以看出,模板和泛型编程过度使用,反而不会达到最好的效果。通常,深度的泛型编程(包括元编程)都是在“迫不得已”的情况下使用的。比如,不使用GP,便会造成巨大的开发工作量(就像货币系统那个案例那样),或者根本无法实现(如标准库中的通用算法)。一般情况下,能使用传统技术,优先考虑传统技术,或者简单的泛型技术。
作为一个好事者(我总是一个好事者&#61514;),我不断地端详XComboBox,发现它像一个东西:容器。没错,ComboBox原本就是一个容器,只是功能更多些而已。既然是个容器,我是否可以把它用在标准算法上呢?当然可以。我们可以把它看作是一个vector或者dequeue或者list,也可以象操作序列容器那样操作XComboBox,只需为XComboBox增加一组成员和typedef:
template <typename   ItemT,   typename   DispOP>
class   XComboBox
{
public:

Iterator   begin(){…}
Iterator   end()   {…}
void   push_back(const   ItemT&   item)   {…}
void   pop_back()   {…}

};
这样,我们便可以将一个容器中的业务对象直接copy到XComboBox中去:
XComboBox <Admin>   combo1;
vector <Admin> vAdmins;
…//初始化vAdmins,比如来源于某种对象数据系统
copy(vAdmins.begin(),   vAdmins.end(),   back_inserter(combo1));


更进一步,如果我们使用的数据库结果集也兼容标准库容器。那么,可以用transform算法直接将一个结果集传递到XComboBox中:
struct   RS_to_Admin
{
Admin   operator()(const   STLRecordSet::RowT&   row)   {
Admina;
…//用结果集的行初始化业务对象
returna;
}
};
XComboBox <Admin> combo1;
STLRecordSet   rs=QueryData(“…”);
tramsform(rs.begin(),   rs.end(),   back_inserter(combo1),   RS_to_Admin());
这些也表明,一旦所有的应用组件,包括数据访问、界面元素等等,都STL化之后,整个应用系统的开发便可以大幅度的简化。在这方面,走在前面的是Mathew   Wilson(他的《Imperfect   C++》想必很熟吧)。Wilson所开发的STLSoft以及相关的STLxxx系列库为诸多系统包括Unix、MFC、ATL、Internet、COM、.net提供了STL化的包装层。使得标准算法得以发挥最大的作用。


[解决办法]
支持LZ的研究!:)

界面组件是否需要模版化,这是一个两难课题。
一方面,正如LZ所说,弱类型意味着必须强制类型转换,而强转是许多错误的根源;
另一方面,模版化确实加大了界面组件的复杂度;并且,这种复杂度是否必要呢?

除了这两种做法,有以下思路可以参考:
1、参考MVC的做法(当然不是照搬,只是借鉴它的解耦方法),由容器存储数据,由界面组件显示数据,再由专门的操作类将数据从容器中注入界面组件以显示。这样,界面组件可以专注于显示;毕竟显示的复杂度已经比较大,再加入GP感觉有些偏重了。
2、界面组件不使用类似void*的弱类型关联方式,使用固定的一种或几种关联(其中一种可以是long型的ID,其他几种也不能有类似void*的弱类型)。这样就可以减少出错了。

以上两点可以分别使用,也可以一起使用。

但是,这两点也都有局限性。第一点形成了包含三个部分的一个小框架,虽然其中两部分可以重用现有的界面组件及容器类,但是仍然是增加了复杂度。而且,如果要做得比较规范,其操作类还是要用GP手段。所以综合看来,复杂度没有下降,只是增加一个中间层,使界面组件和数据解耦而已。
第二点是牺牲灵活性以换取安全性的做法。其局限性就不必多说了。

所以在界面这方面如何取舍,是比较难做决定的。这可能就是C++没有提供标准界面库的原因之一。当然LZ提供的思路值得借鉴。这里没有谈及它的好处,只是因为这些都已经在LZ的文中一览无余,无须我画蛇添足了,呵呵。
[解决办法]
我不断地端详XComboBox,发现它像一个东西:容器。
很佩服LZ的思维

我想LZ的意思是
在我们的意识中
C++不适合做上层的开发(在开发效率的角度看待问题)
但是如果我们能善于发现问题
并且善于解决问题
你比如说LZ把STL中的算法运用到上层开发中
是可以提高开发效率的

[解决办法]
回复人:xenix(早死三年何愁睡) ( 二级(初级)) 信誉:100 2007-6-14 19:03:38 得分:0
?

两大泛型界面库: win32gui和smartwin,有真正的用户吗?几乎没有

=============================================================
有,我就在用赫赫
[解决办法]
谁说 GUI 就不是所谓“高端”?

问这种问题的人是不是学计算机的
你们是不是认为 GUI 很简单呀?

热点排行
Bad Request.