博文

由auto_ptr想到的(2009-09-28 19:59:00)

摘要:由一份auto_ptr源代码所引发的思考
   Kyle
   CPPCN 版权所有
  如果我问你,auto_ptr最关键的地方,或者说它与一般指针最不一样的地方在哪里,你一定会说它是一个对象,它与自己所占有的资源密切相关,当自己消亡的时候,它也会将自己所拥有的资源一同释放。很好,它之所以会拥有这一特性,全都要归于析构函数的功劳,在析构函数中,它会进行善后处理,这样也就很好的避免了资源泄漏。当然,引入auto_ptr的原因还不至于这么简单,因为许多资源泄漏的问题主要是由于我们的粗心大意所致,所以只要我们细心一些,还是会避免一些不该发生的资源泄漏问题。那究竟是什么原因让我们引入auto_ptr呢?对了,你一定也想到了,就是在异常处理的时候。Ok,来看下面这个例子:
  void foo()
  {
  ClassA *ptr=new ClassA;
  try
  {
  /* 此处放置可能抛出异常的操作 */
  }
  catch(…)
  {
   delete ptr;
   throw;
  }
  delete ptr;
  }
  上面这个例子使用了一般的指针,你会发现它防止发生资源泄漏的处理是多么复杂,必须在每个catch中进行资源的释放,而且有可能会有许多的catch。天哪,这简直就是灾难,它会使我们的代码很长,不易维护,如果忘记其中的一个,就会产生莫名其妙的错误。既然一般的指针防止资源泄漏会如此的繁琐,那有没有一个办法可以使我们不必操心资源的释放呢,由于在异常发生的时候,会进行堆栈解退,所以我们不必担心作为局部变量的指针本身不被销毁,既然这样,我们干脆建立一个指针对象,好比下面这样:
  template<class T>
  class auto_ptr1
  {
  private:
  T* ap;
  public:
   ……..
  ~auto_ptr(); //资源释放
  }
  当指针被销毁的时候,必然会执行析构函数,那就在析构函数中进行资源的释放不就ok了,呵呵,怎么样,是不是很简单呢?的确,整个逻辑的确很简单,但是如果我们在深入思考一下这个指针对象......

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

qt+vs2003安装(2009-02-05 23:45:00)

摘要:本来一直用开源版的,配合mingw也不错,该死的xp崩溃了,重装mingw觉得比较麻烦,正好手上的vs2003光盘一直没用过,就装了个vs2003,既然vs是d版,干脆使用d版的qt好了.我哭,兴致冲冲的打开传说中qt.tttxp.ru显示Site was closed according to request of Trolltech ASA company.好后悔当初没down下来~~
不过这世界上还有google,在一个国外网站上找到了种子。我需要qt-win-commercial-src-4.3.1.zip,qt-vsintegration-1.3.1.exe,还有一个license.txt文件.下载完后,进入vc的控制台环境,编译源代码才能用,照着附带的readme做就是了。安装qt-vsintegration,他是VC的插件,装上后就能创建QT的VC项目,比用一般的编辑器来写QT程序要方便许多。还要记得把把 license.txt 存成 .qt-license :
命令行下copy license.txt .qt-license
ok,最后虚伪的说一句,支持Qt,商业应用请购买正版,现在qt归诺基亚了,不知道前途如何!
这里还是把步骤及下来:
1.我的电脑上右键->属性->高级->环境变量
在环境变量path中添加;D:\qt\bin (;是用来和其它环境变量分开的)
新建环境变量:QMAKESPEC值win32-msvc.net 2.解压qt-win-commercial-src-4.3.1.zip到d:\qt 3.检查d盘空间
ntfs下占用空间为949 MB (995,687,822 字节) , 带文件压缩的情况下
fat32下大小为1.96 GB (2,108,853,199 字节) , 实际占用空间会更多
另外考虑到盘上需要留一定的空间出来, 4.把d:\qt下的.LICENSE文件复制到系统盘的用户目录下
如C:\Documents and Settings\Administrator 5.开始->所有程序->Microsoft Visual Studio 2003->Visual Studio .NET 2003 Command......

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

浅谈c++中的this指针(2008-02-21 13:00:00)

摘要:有下面的一个简单的类:

class CNullPointCall
{
public:
static void Test1();
void Test2();
void Test3(int iTest);
void Test4();

private:
static int m_iStatic;
int m_iTest;
};

int CNullPointCall::m_iStatic = 0;

void CNullPointCall::Test1()
{
cout << m_iStatic << endl;
}

void CNullPointCall::Test2()
{
cout << "Very Cool!" << endl;
}

void CNullPointCall::Test3(int iTest)
{
cout << iTest << endl;
}

void CNullPointCall::Test4()
{
cout << m_iTest << endl;
}
那么下面的代码都正确吗?都会输出什么?

CNullPointCall *pNull = NULL; // 没错,就是给指针赋值为空
pNull->Test1(); // call 1
pNull->Test2(); // call 2
pNull->Test3(13); // call 3
pNull->Test4(); // call 4
你肯定会很奇怪我为什么这么问。一个值为NULL的指针怎么可以用来调用类的成员函数呢?!可是实事却很让人吃惊:除了call 4那行代码以外,其余3个类成员函数的调用都是成功的,都能正确的输出结果,而且包含这3行代码的程序能非常好的运行。
经过细心的比较就可以发现,call 4那行代码跟其他3行代码的本质区别:类CNullPointCall的成员函......

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

c++内存管理(2008-01-18 12:41:00)

摘要:程序员们经常编写内存管理程序,往往提心吊胆。如果不想触雷,唯一的解决办法就是发现所有潜伏的地雷并且排除它们,躲是躲不了的。本文的内容比一般教科书的要深入得多,读者需细心阅读,做到真正地通晓内存管理。 1、内存分配方式

  内存分配方式有三种:
  (1)从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量,static变量。
  (2)在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  (3) 从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

  2、常见的内存错误及其对策
  发生内存错误是件非常麻烦的事情。编译器不能自动发现这些错误,通常是在程序运行时才能捕捉到。而这些错误大多没有明显的症状,时隐时现,增加了改错的难度。有时用户怒气冲冲地把你找来,程序却没有发生任何问题,你一走,错误又发作了。 常见的内存错误及其对策如下:
  * 内存分配未成功,却使用了它。
  编程新手常犯这种错误,因为他们没有意识到内存分配会不成功。常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc或new来申请内存,应该用if(p==NULL) 或if(p!=NULL)进行防错处理。

  * 内存分配虽然成功,但是尚未初始化就引用它。
  犯这种错误主要有两个起因:一是没有初始化的观念;二是误以为内存的缺省初值全为零,导致引用初值错误(例如数组)。 内存的缺省初值究竟是什么并没有统一的标准,尽管有些时候为零值,我们宁可信其无不可信其有。所以无论用何种方式创建数组,都别忘了赋初值,即便是赋零值也不可省略,不要嫌麻烦。

  * 内存分配成功并且已经初始化,但操作越过了内存的边界。
  例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在fo......

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

关于C++中继承、重载、掩盖(转)(2007-12-07 21:12:00)

摘要:在开始之前,我们可以先看下面这个小程序: #include <iostream> using namespace std;   class Base { private:        int x; public:        virtual void mf1()        {               cout<<"Base: mf1()"<<endl;        }        virtual void mf1(int)        {               cout<<"Base: mf1(int)"<<endl;        }        virtual void mf2()        {               cout<<"Base: mf2()"<<endl;        }        void mf3(double)   &......

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

boost conrersion library(2007-08-03 16:14:00)

摘要:一、与多态类型相关的转换 1、 polymorphic_cast与dynamic_cast dynamic_cast可以安全地将一个指向多态对象的指针向下转换为派生类指针。但是,当dynamic_cast转换失败时,返回的是NULL,也就是说,dynamic_cast的转换成功与否是在运行期确定,而不像其他C++内建cast那样在编译期确定。换言之,如果在进行dynamic_cast之后,不检测返回值,就等于埋下了一个定时炸弹。 而Boost的polymorphic_cast在dynamic_cast的基础上增加了对返回值的检测,如果转换失败,它就会抛出std::bad_cast。 2、 polymorphic_downcast与dynamic_cast 但是抛出异常会降低程序的效率,而且dynamic_cast更会查询一个type_info结构来确定正确的类型,所以不管是空间上的成本还是时间上的成本,都会大大增加。 polymorphic_downcast可以把成本降低到最低,甚至是编译期上。不过使用这个转换必须非常的小心。 //#define NDEBUG #include using namespace std; struct A{ virtual ~A(){} }; class B:public A{}; class C:public A{}; int main(){ A *pa=new C; B *pb=polymorphic_downcast(pa); } 注意上面没有定义NDEBUG(或编译器的调式模式),这样才能在调试时,判断polymorphic_downcast是否成功。最后,当一切OK的时候,再将定义NDEBUG(或关闭编译器的调试模式)。这样在最终的软件中,polymorphic_downcast即保证了安全的转换,又保证了低运行成本。 3、 窥其内部 template inline Target polymorphic_cast(Source*x){ Target tmp = dynamic_cast(x); if ( tmp == ......

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

用标准c++实现string与各种类型的转换(2007-08-03 15:30:00)

摘要: 要实现这个目标,非stringstream类莫属。这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本。简单起见,我主要以stringstream为中心,因为每个转换都要涉及到输入和输出操作。示例1示范怎样使用一个stringstream对象进行从 string到int类型的转换 注意,使用string对象来代替字符数组。这样可以避免缓冲区溢出的危险。而且,传入参数和目标对象的类型被自动推导出来,即使使用了不正确的格式化符也没有危险。 示例1: std::stringstream stream; string result="10000"; int n = 0; stream > n;//n等于10000 int到string类型的转换 string result; int n = 12345; stream ......

阅读全文(5868) | 评论:4

引用总结(2007-07-26 16:19:00)

摘要:什么是“引用”?申明和使用“引用”要注意哪些问题? 引用就是某个目标变量的“别名”(alias),对应用的操作与对变量直接操作效果完全相同。 申明一个引用的时候,切记要对其进行初始化。引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,不能再把该引用名作为其他变量名的别名。声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。不能建立数组的引用。 2. 将“引用”作为函数参数有哪些特点? (1)传递引用给函数与传递指针的效果是一样的。这时,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象(在主调函数中)的操作。 (2)使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作;而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本;如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据较大时,用引用比用一般变量传递参数的效率和所占空间都好。 (3)使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用"*指针变量名"的形式进行运算,这很容易产生错误且程序的阅读性较差;另一方面,在主调函数的调用点处,必须用变量的地址作为实参。而引用更容易使用,更清晰。 3 在什么时候需要使用“常引用”?  如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。常引用声明方式:const 类型标识符 &引用名=目标变量名; 例1 int a ; const int &ra=a; ra=1; //错误 a=1; //正确 例2 string foo( ); void bar(string & s); 那么下面的表达式将是非法的: bar(foo( )); bar("hello world"); 原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都......

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

explicit关键字的作用(2007-07-26 16:13:00)

摘要:c++中的explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?

如果c++类的构造函数有一个参数,那么在编译的时候就会有一个缺省的转换操作:将该构造函数对应数据类型的数据转换为该类对象,如下面所示: class MyClass
{
public:
MyClass( int num );
}
....
MyClass obj = 10; //ok,convert int to MyClass在上面的代码中编译器自动将整型转换为MyClass类对象,实际上等同于下面的操作: MyClass temp(10);
MyClass obj = temp;上面的所有的操作即是所谓的"隐式转换"。

如果要避免这种自动转换的功能,我们该怎么做呢?嘿嘿这就是关键字explicit的作用了,将类的构造函数声明为"显示",也就是在声明构造函数的时候前面添加上explicit即可,这样就可以防止这种自动的转换操作,如果我们修改上面的MyClass类的构造函数为显示的,那么下面的代码就不能够编译通过了,如下所示: class MyClass
{
public:
explicit MyClass( int num );
}
....
MyClass obj = 10; //err,can't non-explict convert......

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

c++强制类型转换小结(2007-07-24 23:16:00)

摘要:  标准c++中主要有四种强制转换类型运算符:
const_cast,reinterpret_cast,static_cast,dynamic_cast等等。 1)static_cast<T>(a)
将地址a转换成类型T,T和a必须是指针、引用、算术类型或枚举类型。
表达式static_cast< T > ( a ) a的值转换为模板中指定的类型T。在运行时转换过程中,不进行类型检查来确保转换的安全性。 例子: class B { ... };
class D : public B { ... };
void f(B* pb, D* pd)
{
    D* pd2 = static_cast<D*>(pb);        // 不安全, pb可能只是B的指针     B* pb2 = static_cast<B*>(pd);        // 安全的
    ...
} 2)dynamic_cast<T>(a)
完成类层次结构中的提升。T必须是一个指针、引用或无类型的指针。a必须是决定一个指针或引用的表达式。
表达式dynamic_cast< T >( a ) 将a值转换为类型为T的对象指针。如果类型T不是a的某个基类型,该操作将返回一个空指针。 例子:
class A { ... };
class B { ... };
void f()
{
   A* pa = new A;
   B* pb = new B;
   void* pv = dynamic_cast<void*>(pa);
   // pv 现在指向了一个类型为A的对象
   ...
   pv = dynamic......

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