当前位置导航:炫浪网>>网络学院>>编程开发>>C++教程>>C++进阶与实例

C++关键问题指导

“磨刀不误砍柴工”这句老话用在C++身上是再合适不过了。如果把C++比喻成一把刀,那么它会是一把材质和形状都非常好的刀——只是没有开锋。所以我们要“磨刀”。
C++这把刀材质坚硬,强度也高,或许还进行过表面处理。那自然很难磨,费时费力。不过,一旦磨好,便锋利无比,持久耐用。这还是值得的。
C++的“磨刀”实际上就是开发库,各种可能的库,从基础库开始,到各类应用库。库越多,刀磨得越快。当然了,开发库是有代价的。需要花时间,花精力,以及无限的耐心。
此时,我们便需要做一些估计和四则运算,以便选择如何磨这把刀。
最关键的因素,是某件工作被重复的次数,或者近似的工作的数量。某件工作被重复的次数的含义很明显,如果一再重复自己已经做过的事,明显是愚蠢的行为。“copy-paste神功”利用源代码的可复制性,很容易地避免了重复编码。但是,这也只是稍稍“不那么愚蠢”而已。
当这些被重复的代码发生变化,那么,每一处paste的地方都需要被修改或替换。于是,聪明的人们发明了子程序、函数、类、继承、多态、模板等等五花八门的技术手段。目的便是消除这种“愚蠢”或“不那么愚蠢”的做法。一旦某件工作被做成子程序、函数、类、模板等,实际上便形成了一个库,只是库的应用范围有所差异而已。
相比之下,近似的工作的含义则复杂、含混得多。我们编码时,时常会发现某些工作具有不同程度的相似性。比如,我们写一个排序算法,用于int类型;下次写同样的排序算法,用于double类型;…。有多少需要排序的类型,就要写多少次算法。这些算法并非完全相同(在类型上有所差异),但其结构完全一样。由此,我们可以用一个泛型算法实现所有类型的排序(暂不考虑性能问题和类型concept需求)。
当然,并非所有的代码都具有如此高的相似性。代码的相似性越少,创建抽象的库代码的难度越大。所以,库是有限度的。综合考虑创建库的代价和效用,便可以指导我们是否建立库,或者如何建立库。
对于完全一模一样的代码重复,自不必说,只管做成库代码。因为做这些库代码的工作量,只比编写一次代码的工作量多那么一点。修改也是如此。
而对于相似的代码,情况则复杂得多。一般而言,如果这些相似代码仅有少量的出现,比如3、4处,通常没有必要创建相应的库代码。特别是这些相似代码的相似程度较小,或者代码复杂的时候。此时,创建库的代价很大,但获得的收益也仅有这么3、4处而已。
但必须指出的是,我们在考虑是否创建库时,还必须认真地考虑其他项目,或者未来项目中应用的可能性。如果是某个非常常用的功能,尽管在当前项目中只出现一次,考虑到未来其他项目的应用,也应当将其开发成库。
回到砍柴的比喻。一把没有开封的刀,在一定程度上也能砍下一些树枝,只是砍起来费劲些,也无法砍下较粗的树枝。如果我只需要砍那么几根枝丫,不需要很多,而且以后也不会再去砍柴。那么,一把钝刀也够用了。在这种情况下,如果还费劲地磨刀,着实是一种浪费。相反,如果我今天要砍一整担柴火,或者需要日复一日地砍柴。那么,我最好还是把刀磨磨好再说。
磨刀也有难有易。材质坚硬(俗称“钢火”好)的刀,磨起来费力。但更锋利,更耐用。材质较软的刀,尽管磨起来快。但要使其锋利和耐用,比较困难。(因为材质软的刀,在磨到一定程度后,刃口会向上卷起。如果再反过来磨,又会向反方向卷)。 [Page]
C++就属于那种材质坚硬的刀。(而且生产厂家出于成本考虑,也没有为其开锋)。于是,作为“职业砍柴人”,有必要好好地磨砺一下这把好刀。(当然啦,也有很多“职业砍柴人”转而使用那些容易磨,或者出厂时已经开锋的“软质刀”)。
磨刀也是有讲究的。(呵呵,我自认为在磨刀方面还是有那么一两手的)。越是硬的刀,越是不能急,一般需要循序渐进。为了不耽误柴火的产量,只能磨一点,用一点。一开始先用大角度,在锋口上磨出快口。尽管大角度的锋口不如小角度的来的锋利,但要比没开锋来得好。更重要的是,大角度锋口所花的时间要比小角度的少很多。由于我手中的是一把好刀,在砍柴的过程中,基本上不会有什么损耗。等到第二天,我再以小角度磨刀。同样,也不打算在第二天就全部搞定,继续用磨了一半的刀砍柴。经过第二天的磨砺,刀会比第一天好用些。然后第三天同第二天一样。以此类推,直到若干天后,刀完全磨好。此后,只需定期打磨一下,维持刀具的锋利即可。相比之下,那些软质的刀则需要更频繁地磨,以维持锋利程度。
好了,刀就磨到这里吧。我们来看看如何“磨”C++。这里就用一个现实的案例来加以说明吧。
现在很多应用软件,特别是MIS类软件,都需要访问数据库,然后把数据提取出来,进行进一步加工,或者直接显示在界面上。下面这样的代码,在软件中想必是随处可见的:
void OnQueryClicked()
{
DataConnection dc_(…);
Rowset rs_(dc_, “select … from …”);
int j(0);
m_resGrid.Clear();
m_resGrid.SetColNumber(rs_.ColNumber());
while(rs_.MoveNect())
{
for(int i=0; i<rs_.ColNumber(); ++i)
{
m_resGrid.AddRow();
m_resGrid.Cells(j, i)=rs_.field(i);
}
++j;
}
}
很常规的代码。这里m_resGrid是一个界面上的Grid控件。同时也假定rs_已经采用字符串绑定,可以直接输出字符串。
现在,让我们发挥一下想象力,如果结果集rs_和Grid控件m_resGrid都支持标准STL容器的接口,事情会怎样?很简单,我们可以采用以下的方式重写上述代码:
void OnQueryClicked()
{
DataConnection dc_(…);
Rowset rs_(dc_, “select … from …”);
int j(0);
m_resGrid.Clear();
m_resGrid.SetColNumber(rs_.ColNumber());
std::transform(rs_.begin(), rs_.end(), std::back_inserter(m_resGrid), CopyRStoGrid());
}
明显简单多了,不是吗?不过有问题。这个transform仅仅替代了原来的外层循环,即那个while()。而内存循环,也就是rs_的Column上的循环还没有做。
解决的方法也不复杂,也就是CopyRStoGrid的作用:一个执行内循环的函数或函数对象即可:
struct CopyRStoGrid

共2页 首页 上一页 1 2 下一页 尾页 跳转到
相关内容
赞助商链接