-
B.S 举的一个结合Generic和OO两种设计思维的经典例子(CC) - [C++]
2006-12-05
Bjarne: 如果你面对的问题既需要某些RTTI功能(需要面向对象编程),又具有一些能够从编译期决议中获益的方面(泛型编程的用武之地)的话,那么你就需要将面向对象编程和泛型编程结合起来。
将一个保存了shape的容器中的所有元素都显示出来就属于这类问题。考虑以下代码:
void draw_all(vector<Shape*>& vs)
{
for (int i=0; i<vs.size(); ++i) vs[i]->draw();
}我猜想这并不能被看作纯粹的面向对象编程,因为我直接利用了“vs是一个装有Shape*元素的vector”这个事实。毕竟,类型的参数化通常是被认为属于泛型编程的范畴。我们也可以消除这种对静态类型信息的使用(所谓“不纯粹的面向对象编程”):
void draw_all(Object* container)
{
Vector* v = dynamic_cast<Vector*>(container);
for (int i=0; i<v.size(); ++i)
{
Shape* ps = dynamic_cast<Shape*>(v[i]);
ps->draw();
}
}但凡鼓励以上这种风格的语言,其语法通常都比较漂亮,然而这个例子却说明了当你把静态类型信息的使用减至最小的时候发生了什么。如今,在C++或其它语言中,仍然有人在使用这种风格。我只是希望他们在错误处理方面有系统化的准备。
在前一个例子中,vector<Shap*>依赖于对泛型编程的一个最简单的运用:vector的元素类型被参数化了,而且我们的示例代码正获益于此。在这个方向上我们还可以走得更远,即推而广之到所有标准库容器身上:
template<class Container> void draw_all(Container& cs)
{
for (typename C::iterator p=cs.begin(); p!=cs.end(); ++p)
(*p)->draw();
}例如,这段代码就既可以作用于vector上,又可以作用于list上。编译期决议确保我们不用为这种泛化处理付出任何运行期额外代价。我们还可以通过在draw_all()的使用接口中运用迭代器,从而进行进一步的泛化处理:
template<class Iter> void draw_all(Iter fist, Iter last)
{
for (; first!=last; ++first)
(*first)->draw();
}这就使内建数组类型都得到了支持:
Shape* a[max];
// 向a中填充Shape*类型的元素
draw_all(a,a+max);我们还可以结合运用标准库算法for_each()和函数适配器mem_fun()来消除显式的循环:
template<class Iter> void draw_all(Iter fist, Iter last)
{
for_each(first, last, mem_fun(&Shape::draw);
}注意,这也是一个关于编译错误信息变得可怕的例子,因为我们并没有显式地表达出我们的假设。例如,我们假设容器里的元素类型为Shape*,然而在代码中,这个假设却相当隐晦。这种情况下我们可以使用约束类(此处为Point_to):
template<class Iter> void draw_all(Iter fist, Iter last)
{
Points_to<Iter,Shape*>();
for_each(first, last, mem_fun(&Shape::draw);
}然而我们又确实很想说明“first和last必须为前向迭代器”:
template<Forward_iterator<Shape*> Iter>
void draw_all(Iter fist, Iter last)
{
for_each(first, last, mem_fun(&Shape::draw);
}这是“concepts”可以大展拳脚的地方之一。
-
使用dtor避免内存泄露 - [C++]
2006-12-04
在使用new 产生局部变量,获取堆栈内的内存时,当后续操作抛出异常,将导致内存泄露出现,纵使在后续试用catch字段抓取所有异常,将之前分配的动态内存一一释放也是难于实现的.
一种简便的方案是将局部变量封装到一个类中。C++保证当离开“}”时,局部变量的析构函数必然调用(C++ F&Q ),因此最合适的一种方案是在局部变量的析构函数内调用内存的释放操作。
标准库提供auto_ptr,协助用户完成以上堆栈内内存的释放操作,因此使用auto_ptr是一种方便的选择,无需用户再自定义类的内存释放的操作,而只需要关注于类的设计。(MEffectiveC++ item 11)
-
封装stringstream的优雅函数,摘自Boost - [C++]
2006-11-27
template
Target lexical_cast(Source arg)
{
std::stringstream interpreter;
Target result;
if(!(interpreter << arg) || !(interpreter >> result) || !(interpreter >> std::ws).eof())
throw bad_lexical_cast();
return result;
}usage:
int i;
......
s = lexical_cast
(i); 是不是够优雅 :)
附:
C/C++字符串格式化方案
sprintf
snprintf
stringstream
strstream
boost::
lexical_cast
标准吗?(-= n/a)
[C90]
是
否(常见扩展)
-
-
-
[C++03]
是
否(常见扩展)
是
是(但是deprecated)
否
[C99]
是
是
-
-
-
C++0x(推测)
是
很可能
是
很可能(也可能仍deprecated)
可能
可用性考虑
易用吗,代码清晰明确吗
是
是
否
否
是
高效吗,无额外内存分配吗
是
是
否
是
否
长度安全吗
否
是
是
是
是
类型安全吗
否
否
是
是
是
是否可用于模板之中
否
否
是
是
是
耗时取样,以sprintf的耗时为基准
Borland C++ 5.5.1/Windows
1.0
1.0
12.6
8.1
19.7
Gnu g++ 2.95.2/Cygwin+Windows
1.0
-
-
2.0
-
Microsoft VC7/Windows
1.0
1.0
13.2
9.0
19.2
Rogue Wave 2.1.1/
SunPro 5.3/SunOS 5.7
1.0
1.1
8.7
4.7
16.5
Rogue Wave 2.2.1/HP
aCC 3.30/HP-UX 11.001.0
1.0
7.9
3.9
9.9
l 如果你所要做的只是将一个值转换为一个字符串(甚至于其他任何类型)的话:尽量默认使用boost::lexical_cast。
l 如果想进行简单的格式化,或者需要支持宽字符串,或者想要让进行格式化的代码能够用在模板当中:尽量使用stringstream或strstream。跟snprintf相比,使用stringstream/ strstream的代码会比较冗长,且难于理解,不过对于简单的格式化任务来说,情况不会太糟。
l 如果想要进行一些更为复杂的格式化任务,同时并不需要宽字符串支持,也不想让代码用在模板当中的话:尽量使用snprintf。这是C的做法,但并不意味着C++程序员就不能用它。
l 仅当实际的性能测试显示那些较好的替代方案在你代码中某个特定的地方确实都会造成瓶颈时,只在这些特定地点改用strstream或snprintf,具体用哪个则要看谁更适合。
l 永远不要使用sprintf。
-
试用CVS/WinCvs(一) - [C++]
2006-11-24
在RH8.0上配置CVS服务器:
1。确定2401 端口的防火墙一定关闭,事实上,如果在配置WorkStation的机器上配置medium防火墙,WorkStation的服务器功能都会被关闭掉,使用iptables-save可以看出。
2。CVS服务和telnet,ssh等一样是xined下的守护进程,你也可以使用stand-alone方式使用,使用xinetd方式,需要在/etc/xinetd.d/下配置cvspserver配置文件,并重启xinetd服务
-
i386, i686的区别 - [Linux]
2006-11-24
Linux 2.4.18-24 i686 i686 i386
这三个的含义在于
machine hardware name:i686
processor type:i686
hardware platform:i386通常,标有i686的RPM包能在奔腾二代以上的CPU上执行,支持i686的机器向下兼容i386的包,只是不能发挥更佳的效能。而硬件只支持i386的处理器平台,则不能上i686的RPM包







