博文

[笔记]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......

阅读全文(3127) | 评论:1

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),可以想像成几乎不需要时间.如果空闲链空,则需从后备池中拿一块出来,如果后备池中非空......

阅读全文(6347) | 评论:0

[笔试]奇怪的多态(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:一个基础的一时弄错了,记住了,先构造基类,析构是相反的过程。......

阅读全文(4369) | 评论:9

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上会给出个警告,因为在语义上是重复的。......

阅读全文(8214) | 评论:2

我写的智能指针auto_ptr(2007-03-20 17:37:00)

摘要:为什么要用这个auto_ptr,一般的C语言指针不够用吗?如果C++里没有new,那一般的指针就够用了,现在的代码都像这样: int *p=new int[100]; //使用p delete[] p; 使用p的时候,如果突然出了问题,比如丢出异常,就会中止程序,而不能运行delete,造成内存泄漏,所以在使用的时候,经常要做很多判断,比如 if(p)
{
   ...
   delete[] p;
} if(XXX)
{
   delete[] p;
   throw(xx);
} 所以,拿到内存容易,但责任就大啊,所以整个代码被整个乱七八糟,而且程序员还要保持清醒的头脑,一不小心就。。。 STL的auto_ptr就是为了解决这个问题,做的一个封装,在指针的运算过程中,比如*,->,发生错误,丢异常,并在析构的时候delete。 把VC6里的auto_ptr复制下: template<class _Ty>
 class auto_ptr {
public:
 typedef _Ty element_type;
 explicit auto_ptr(_Ty *_P = 0) _THROW0()
  : _Owns(_P != 0), _Ptr(_P) {}
 auto_ptr(const auto_ptr<_Ty>& _Y) _THROW0()
  : _Owns(_Y._Owns), _Ptr(_Y.release()) {}
 auto_ptr<_Ty>& operator=(const auto_ptr<_Ty>& _Y) _THROW0()
  {if (this != &_Y)
   {if (_Ptr != _Y.get())
    {if (_Owns)
&nbs......

阅读全文(6955) | 评论:3