博文
boost::assign让容器赋值更简单(2008-02-05 16:01:00)
摘要:分析源码其实很有意思,尤其是优秀的源码。boost::assign库,老实说,设计初衷有些无聊,它无非是想让容器的赋值(插入)操作更加简单,简单得就像脚本语言一样,比如,原来的STL里的容器有各种各样的,不同的容器会有不同的赋值方式,比如:
#include<vector>
using namespace std:
int main()
{
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
return 0;
}
如果你不觉得敲‘.push_back’多么麻烦,大可不必管这个库,实际上这样写,机器看起来更简单。而boost就想办法让人偷懒,而机器转一些弯,成了这样:
int main()
{
vector<int> v;
v+=1,2,3;
return 0;
}
能够运行的示范代码我没有找到啊,只是写个样子出来,OMG,真像脚本语言!你要问,效率如何?可能会牺牲一些,就像,我问你,仿函数比函数的调用效率是不是低一些,如果是,那STL里还不是大量用仿函数?但这些差别,绝对不是相差很大,不会有质的差别。
好了,下面进行分析,我不是设计者,不知道大哥他是怎么想的,我只从分析的角度说明。
1,设计各种仿函数来管理具体的容器和它的赋值方法
仿函数比函数好的一个地方,它可以有成员变量,而这里就关联了这个具体的容器。在源码的assign/list_inserter.hpp里的前面部分,有一个namespace assign_detail命名空间,在那里定义了各种,仿函数,比如调用push_back的
template< class C >
class call_push_back
{
C& c_;
......
boost::any实现一个容器里放多种类型的对象(2008-01-31 23:44:00)
摘要:STL里的容器要求只能是一种类型,比如
vector<int> v;
这个数组里就只能放int型数据,换了其它的数据,一不认识,二如果大了还放不下,当然,大多数情况不需要这样的功能,我要的就是一个int的数组,我疯了还往里面push一个浮点数?我也暂时没想到需要在一个容器里放多种类型数据的应用场合,至少这种要求也并不过分,就当是考验一下C++,它能做到吗?
boost::any库肯定的回答了这个问题,从名字上看,它能放下任何类型的数据,口气不小.可以这样定义:
using namespace boost;
vector<any> v;
v.push_back(any(100));
v.push_back(any(3.14));
boost的源码是免费的,下来打开看就知道了,整份源码只有200多行,很简洁很强大!它内嵌有一个基类placeholder,不做任何事,然后还有一个模板类holder<T>,它从placeholder派生下来,holder<T>是对T的一个封装,有一个成员就是T held,数据本身,然后在any类里面有一个placeholder的指针,基类指针可以指向任何子类成员,即holder<T>的对象,而holder<T>可以具现成任何类型T的一个holder - - (很简洁很强大吧)
画个图
placeholder指针->
任意类型的holder
holder<int>, holder<float> ...
这些都容易,但有一个问题,在需要一个容器放多种类型数据的时候,需要做一件事,运行时类型检查,vector的设计没有考虑这个,所以这件......
traits编程技法和模板元编程基础(2008-01-30 14:16:00)
摘要:哈,我发现我终于成为标题党啦,全拿一些powerful的词做标题,骗点击率。说实话,在学校写论文全是有目的的,保研,升学,拿学位等,其实写的都是些什么哦,这里抄那里抄,能把专家组唬到就是好文章,写博客不同,我爱写就写,你当我是炫耀也好,轻浮也罢,都不放在耳边。
C++语言其实一点都不强大,强大的是那些使用C++的牛人们!
1,traits技法
这个说法我是从《STL源码剖析》中看到的,但是真正明白还是后来慢慢想才明白的。traits的意思是‘特性’,《Eff C++》里说将类的设计视为类型的设计,把class的设计当成type的设计,C++提供了一种无限的可能,使你可以不断的设计新的类型,从而使语言本身更强大。面对各种class,语言本身也没有提供太多的东西去描述‘特性’,有些知识是一开始不可预料的,所以没办法定义,比如,可拷贝性。如果语言定义一开始就很注重这个概念,那可能会多一个关键字操作符,譬如copiable()。在C的时代有sizeof(),在C++的时候有typeof,开玩笑,是typeid,但是这是在运行时,但是有了traits技法,这个是不必要的,将这些事情给编译器在编译期运行,会给运行程序带来更多的效率。我们知道型别本身这个信息也可以看成是基本的‘特性’。
在编译期做‘特性’检查,就是做类似这样的事情:
template<typename T>
void doSomething(T& t)
{
if(t 具有某种特性)
{
做一些事情
}
else
{
做另一些事情
}
}
在运行期做,可以这样设计T
class T
{
public:
T() : copiable(true) {}
virtual ~T() ......
[笔记]什么是封装(2008-01-24 23:48:00)
摘要:初学COM,突然觉得,这真是个好东西,天啦,我终于开始学十几年前的微软技术了。。。
脑子里有太多东西,不知道从哪里记,就从封装开始吧。大学里学的是C(因为专业问题),但是我自学的是C++,我当时以为C++是C的扩充,就像C2。0一样。说C++是面向对象,C是面向过程,这一年级的学生都能脱口而出,但到底为什么要面向对象,为什么数据结构+算法的模型不能胜任现在的大型软件开发?面向对象把它变成了
对象=数据结构+算法
程序=对象1+对象2+。。。
为什么不能胜任?如果你做为开发者,然后去听一下那些完全不懂计算机的客户老板对项目的不断改变的需求,甚至到无耻的地步的时候,你就会彻底抛弃你对C编程的自信。
我没有说C不好,在很多方面,C比C++好,C++就好像迈了一步,但是还是有很多问题需要解决,一条脚还在原地。
对象就是对数据结构和算法的封装的实例。把数据和方法绑在一起,就叫一个类,类一实例化,就是对象。从代码上看,就像这样
class A
{
int data;
public:
void foo();
};
Thinking in C++一书里前面部分专门讲,这样做的来源,因为很早C程序员就是这样想的,只是形式上没有这样做,比如
struct A
{
int data;
};
void foo(struct A*);
这有什么区别?除了好看些,名字可以有更好的一些作用约束之外,似乎没有任何进步啊。同样,C++的编译器也正是这样做的,将上面的class A进行sizeof(A)运算,结果是4,那个成员函数被编译器移出了类,非常巧妙地欺骗了天真的C++初学者。同时,编译器在调用成员函数的时候,将this指针偷偷换进去,实质上这和C的代码一模一样,而一年级的学生可能会因为自己学了C++,能写出一些用类描述的程序,而觉得非常了不起。
你看不下去了,我这里没有干系到类的继承,你要说多态啊,没有多态哪里是面向对象?很多人这样说。可不可以先不想后面的事情,为什么要用类,为什么要用面向对象,都不明不白,就接着讨论,是不是有点像站在空中说话一样,一点不踏实。
我要说的封装,应该是,接口和实现的分开。接口是功能的描述,是交给用户的按钮,实现是设计人员的事情。非常不巧,其实面向......
[笔记]template class的隐式转化(2007-12-30 18:29:00)
摘要:[Effective C++ 3rd 条款46]
有理数类class Rational是一个template<class T>,T的意思是做为表示分子和分母的数值类型.为了让它能够有相乘运算,可以写个函数
template<class T>
const Rational<T> operator*(const Rational<T> &l,const Rational<T> &r)
{ ... }
为了让它能够自动进行隐式转化,因为它有一个non-explicit的构造函数
template<class T>
class Rational
{
Rational(const T& n = 0, const T& d = 1){...}
};
所以希望这样的代码可以通过编译:
Rational<int> onehalf(1,2);
Rational<int> result=onehalf * 2;
因为那个operator*函数是一个模板函数,它并不等同于Rational<int>具现化后的那个函数,所以不能做隐式转化,实际上它不能通过参数推导.
解决方法是将该函数在template<class T>class Rational{}内申明为friend,因为在编译器看到Rational<int>的时候,就将Rational<int>具现出来,所以那个friend申明会爆光.
template<class T>
class Rational
{
...
friend const Rational<T> operator*(...);
};
然而,还是会引来连接错误,因为爆光的只是申明,对于那个外面的模板函数同样需要有机会具现,才能连接成功.最终,只能再加上内联,让函数的定义在那里爆光.
template<class T>
class Rational
{
...
friend co......
SGI STL中的alloc分析(2007-11-20 20:54:00)
摘要:我不是想重复<<STL源码剖析>>中第2章的内容,顺便广告一下,这本书写得很不错,看完那章回来就从http://www.sgi.com/tech/stl/download.html下载了一份源码,慢慢分析.
1,经典的内存池
为了满足一些小单位的频繁内存配置和回收,如果直接使用malloc()和free()等系统函数,将会使效率很低,同样也会造成很多内存碎片,加重系统垃圾回收的负担.内存池的设计初衷也正在此,将一大片内存一次配置,将小单位内存配置模拟OS的空闲块链,就相当于在中间增加了一层'软分配器',正如在主存和CPU之间增加的cache,小小的投入可以带来性能上的飞跃.但是,我们要问,为什么会这样?
这个问题可以从优秀的SGI STL中寻找答案.
2,SGI STL中的allocater
恰恰这份实现的STL并没有按标准,它是一个无参数的分配器,它有两套配置器,一个是
template <int __inst>
class __malloc_alloc_template;
只是一个简单的封装,其实还是用的C的malloc,从名字就可以看出来,当然它有自己的内存不足时的处理接口.另一个是
template <bool threads, int inst>
class __default_alloc_template;
这是我们需要研究的.可以在内部头文件stl_alloc.h中得到完整的源码.
它维护了一个空闲链的链表,共16个,每一个空闲链的块大小不同,分别是8,16,...,128,对于长为n的配置请求,如果n>128则调用第一套配置器,否则,将n上补成8的倍数,调用对应这16个链中的一个空闲链,从中摘一个块出来,如果链空,则再向后面的内存池请求一大块空间.总的看上去,它不是一个简单的单一的内存池策略,而是一个对不同大小的配置请求进行自适应的分配策略,具体情况如何不多说,下面分析性能.
3,性能的瓶颈在哪?
从应用的角度,操作比较少,简单来说无非是2个,申请allocate(n)和释放deallocate(n),申请的时候,如果空闲链非空,则只需摘一个块下来,速度是O(1),可以想像成几乎不需要时间.如果空闲链空,则需从后备池中拿一块出来,如果后备池中非空......
[笔试]奇怪的多态(2007-10-22 13:49:00)
摘要:今天做的一个题,问你输出是什么:
#include <stdio.h>
class B
{
public:
void f()
{
printf("B.f\n");
g();
}
virtual void g()
{
printf("B.g\n");
}
};
class D : public B
{
public:
void f()
{
printf("D.f\n");
}
void g()
{
printf("D.g\n");
}
};
int main()
{
B *p=new D;
p->f();
delete p;
getchar();
return 0;
}
原来真没看到过,赌了一把,回来用VC、DevC++编译了发现。。。
PS:一个基础的一时弄错了,记住了,先构造基类,析构是相反的过程。......
const指针和引用(2007-10-18 16:58:00)
摘要:很久没写BLOG了,不知道还有没有人对这里有兴趣,呵~
const在面试的时候考得多,特地小结一下。
1,const是修饰符,它表示某个对象是常量,即不能修改,而C++没能彻底抛弃指针这个C留下的遗产而有些尴尬,指针是比较圆滑的家伙,它有双重身份,有时表示它自己,有时表示它所指的对象(所以C++建议我们尽量用引用而不是指针)
const double e;// error
const double pi=3.14;// ok,需要初始化
这样的用法没什么好说的。
2,指针带来的问题
像这样写:
int a,b;
const int *p=&a;
实际上没有定义一个常量指针,而是指向常量对象的指针,这意味着后面可以这样
p=&b;//ok
a,b不是常量而是变量,这是可以的,事实上编译器不管这个,而是管*p和p->这样的运算不能被修改
*p=1;//error
就是说上面那句定义的并不是一个常量,它相当于
int const *p;
实际上是个变量。
而真正的常量指针怎么写?这样
int * const p=&a;//ok
int *const p;// error
它的意思是p不能再改,只能赋值一次,这实际上是什么,是引用。引用和指针有着这样一个巨大的差异。
上面ok的代码,实际上可以写成
int &p=a;
也就是说根本不需要常量指针,因为有引用(?)
3,引用和const
引用有着针指的优点,但是它的身份是确定的,不像指针有双重身份,所以用const修饰没什么问题。
const int &p=a;
它的意思实际上和这样写差不多
int const * const p=&a;
本身一次赋值,所指对象也不能改。这就是喜欢考的那个所谓,一个指向一个常量对象的常量指针。
试一下这么写
int const & const p=&a;
多此一举,在VC上会给出个警告,因为在语义上是重复的。......
[转]内存池(MemPool)技术详解(2007-09-17 17:03:00)
摘要:URL:http://blog.csdn.net/xushiweizh/archive/2006/11/22/1402967.aspx
还有线程池http://www.microsoft.com/china/MSDN/library/netFramework/netframework/NECLRT.mspx?mfr=true
说白了,就是将一些频繁使用的小单位资源集中起来管理和使用,进而大大加大系统的性能。......
[转]SocketAPI,CAsyncSocket,CSocket内幕及其用法(2007-09-16 15:07:00)
摘要:SocketAPI,CAsyncSocket,CSocket内幕及其用法
作者:jmcooler 时间:2001-10-12 10:14 出处:互联网 责编:chinaitpower
摘要:SocketAPI,CAsyncSocket,CSocket内幕及其用法
Socket有同步阻塞方式和异步非阻塞方式两种使用,事实上同步和异步在我们编程的生涯中可能遇到了很多,而Socket也没什么特别。虽然同步好用,不费劲,但不能满足一些应用场合,其效率也很低。
也许初涉编程的人不能理解“同步(或阻塞)”和“异步(或非阻塞)”,其实简单两句话就能讲清楚,同步和异步往往都是针对一个函数来说的,“同步”就是函数直到其要执行的功能全部完成时才返回,而“异步”则是,函数仅仅做一些简单的工作,然后马上返回,而它所要实现的功能留给别的线程或者函数去完成。例如,SendMessage就是“同步”函数,它不但发送消息到消息队列,还需要等待消息被执行完才返回;相反PostMessage就是个异步函数,它只管发送一个消息,而不管这个消息是否被处理,就马上返回。
一、Socket API
首先应该知道,有Socket1.1提供的原始API函数,和Socket2.0提供的一组扩展函数,两套函数。这两套函数有重复,但是2.0提供的函数功能更强大,函数数量也更多。这两套函数可以灵活混用,分别包含在头文件Winsock.h,Winsock2.h,分别需要引入库 wsock32.lib、Ws2_32.lib。
1、默认用作同步阻塞方式,那就是当你从不调用WSAIoctl()和ioctlsocket()来改变Socket IO模式,也从不调用WSAAsyncSelect()和WSAEventSelect()来选择需要处理的Socket事件。正是由于函数accept (),WSAAccept(),connect(),WSAConnect(),send(),WSASend(),recv(),WSARecv()等函数被用......