C++编程思想第一卷第16章P401到P405的一个很奇怪的问题(附有完整代码)

superflytiger 2009-05-22 07:13:06
详细请看我上传的附件。(使用vs2005编译环境)用下面的代码建个工程也行。
现在问题是这样的:
在main()函数中,如果没有把“cout << acStash.remove(0) << endl;” 注释掉,~CleanupCheck()会被调用了很多次。但在所有代码中只有一个静态的CleanupCheck对象,程序结束时,其析构函数应该只被掉用一次才对啊(大多数教材是这样说的),但事实上被调用了很多次,用单步调试,还把栈给冲垮了。我猜这应该是运行时库函数的机制吧。期待高手指点......
//TPStash.h
#ifndef TPSTASH_H
#define TPSTASH_H
#include "../../require.h"

template<class T, int incr = 10>
class PStash
{
int quantity;
int next;
T** storage;
void inflate( int increase = incr );
public:
PStash() : quantity(0), next(0), storage(0) {}
~PStash();
int add( T* element );
T* operator[] ( int index ) const;
T* remove( int index );
int count() const { return next; }
};

template<class T, int incr>
int PStash<T, incr>::add( T* element )
{
if ( next >= quantity )
inflate( incr );
storage[next++] = element;
return ( next - 1 );
}

template<class T, int incr>
PStash<T, incr>::~PStash()
{
for ( int i = 0; i < next; i++ )
{
delete storage[i];
storage[i] = 0;
}
delete []storage;
}

template<class T, int incr>
T* PStash<T, incr>::operator [] ( int index ) const
{
require( index >= 0, "PStash::operator[] index negative" );
if ( index >= next )
return 0;
require( storage[index] != 0, "PStash::operator[] returned null pointer" );
return storage[index];
}

template<class T, int incr>
T* PStash<T, incr>::remove ( int index )
{
T* v = operator[] ( index );
if ( v != 0 ) storage[index] = 0;
return v;
}

template<class T, int incr>
void PStash<T, incr>::inflate ( int increase )
{
const int psz = sizeof ( T* );
T** st = new T*[quantity + increase ];
memset( st, 0, ( quantity + increase ) * psz );
memcpy( st, storage, quantity * psz );
quantity += increase;
storage = st;
}

#endif

//AutoCounter.h
#ifndef AUTOCOUNTER_H
#define AUTOCOUNTER_H

#include "../../require.h"
#include <iostream>
#include <set>
#include <string>

class AutoCounter
{
static int count;
int id;
class CleanupCheck
{
std::set< AutoCounter*> trace;
public:
void add ( AutoCounter* ap )
{
trace.insert( ap );
}
void remove ( AutoCounter* ap )
{
require( trace.erase( ap ) == 1, "Attempt to delete AutoCounter twice" );
}
~CleanupCheck()
{
std::cout << "~CleanupCheck()" << std::endl;
require(trace.size() == 0,
"All AutoCounter objects not cleaned up" );
}
};

static CleanupCheck verifier;
AutoCounter() : id ( count++ )
{
verifier.add ( this );
std::cout << "created[" << id << "]" << std::endl;
}
AutoCounter( const AutoCounter& );
void operator= ( const AutoCounter& );
public:
static AutoCounter* create()
{
return new AutoCounter();
}
~AutoCounter()
{
std::cout << "destroying[" << id << "]" << std::endl;
verifier.remove( this );
}

friend std::ostream& operator<<( std::ostream& os, const AutoCounter& ac )
{
return os << "AutoCounter " << ac.id;
}
friend std::ostream& operator <<( std::ostream& os, const AutoCounter* ac )
{
return os << "AutoCounter " << ac->id;
}
};

#endif

//AutoCounter.cpp
#include "AutoCounter.h"
AutoCounter::CleanupCheck AutoCounter::verifier;
int AutoCounter::count = 0;

//TPStashTest.cpp
#include "AutoCounter.h"
#include "TPStash.h"
#include <iostream>
#include <fstream>
using namespace std;

int main()
{
PStash<AutoCounter, 1> acStash;
acStash.add(AutoCounter::create() );
//把下面的注释去掉后,~CleanupCheck()被调用了很多次
//cout << acStash.remove(0) << endl;

return 0;
}

//require.h
//: :require.h

// From Thinking in C++, 2nd Edition

// Available at http://www.BruceEckel.com

// (c) Bruce Eckel 1999

// Copyright notice in Copyright.txt

// Test for error conditions in programs

// Local "using namespace std" for old compilers

#ifndef REQUIRE_H

#define REQUIRE_H

#include <cstdio>

#include <cstdlib>

#include <fstream>



inline void require(bool requirement,

const char* msg = "Requirement failed") {

using namespace std;

if (!requirement) {

fprintf(stderr, "%s", msg);

exit(1);

}

}



inline void requireArgs(int argc, int args,

const char* msg = "Must use %d arguments") {

using namespace std;

if (argc != args + 1) {

fprintf(stderr, msg, args);

exit(1);

}

}



inline void requireMinArgs(int argc, int minArgs,

const char* msg =

"Must use at least %d arguments") {

using namespace std;

if(argc < minArgs + 1) {

fprintf(stderr, msg, minArgs);

exit(1);

}

}



inline void assure(std::ifstream& in,

const char* filename = "") {

using namespace std;

if(!in) {

fprintf(stderr,

"Could not open file %s", filename);

exit(1);

}

}



inline void assure(std::ofstream& in,

const char* filename = "") {

using namespace std;

if(!in) {

fprintf(stderr,

"Could not open file %s", filename);

exit(1);

}

}

#endif // REQUIRE_H ///:~

...全文
139 10 打赏 收藏 转发到动态 举报
写回复
用AI写文章
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
superflytiger 2009-05-23
  • 打赏
  • 举报
回复
经过仔细看了一下代码,发现问题出在在析构函数中调用了exit(),而且该类的对象是静态的。现在针对问题,写了一个很小的代码,给大家测试一下。不过我还不明白其中的原因,谁对C/C++运行时库比较了解的,弄明白了,记得告诉一下我这个菜鸟哦。
#include <iostream>
#include <cstdlib>
using namespace std;

class Simple
{
public:
Simple() { cout << "default constructor called" << endl << endl; }
~Simple()
{
cout << "destructor called" << endl;
exit(1);
}
};
static Simple simple;//若为静态对象,析构函数会被调用很多次
int main()
{
return 0;
}

/////////////////////////////////////////////////////////////////////////////////////////////
上面的代码如果用单步跟踪调试,还会出现如下的栈溢出信息,由此我猜:析构函数被递归调用了。
Destructor_exit.exe 中的 0x1024fd2f (msvcr80d.dll) 处未处理的异常: 0xC00000FD: Stack overflow
(注:此代码在VS2005下调试运行)
red_berries 2009-05-23
  • 打赏
  • 举报
回复
看一下Crt的源代码就知道了,在main()函数返回好,CRT会调用exit()进行收尾工作,在收尾工作中会调用到你的析构函数,而你在析构函数中又调用了Exit(),这样就进到死循环了,这个对象不一定非要是静态的,全局的一样可以,但局部的就不行了,因为在exit()这个函数里面不会再调用到局部变量的析构函数.还不明白的话就自己看看 CRT源代码,设置下断点看看堆栈你就明白了
panjieC 2009-05-22
  • 打赏
  • 举报
回复
你们看的是中文版还是英文版的啊?
superflytiger 2009-05-22
  • 打赏
  • 举报
回复
非但没有退出,而且对析构函数调用了好多次
goodname 2009-05-22
  • 打赏
  • 举报
回复
程序就彻底退出了。
superflytiger 2009-05-22
  • 打赏
  • 举报
回复
好像跟在~CleanupCheck()中调用的require()中的exit(1)有关。谁对C++的运行时库比较了解的?在析构函数中调用exit()会发生什么.......
liliangbao 2009-05-22
  • 打赏
  • 举报
回复
代码好长啊~
windsting 2009-05-22
  • 打赏
  • 举报
回复
太好了,我也在看这本书,目前在看ch15-多态,估计用不了几天就到ch16了。
等我看到那里的时候好好看看,如果到时候你的问题还没有得到解答,我可以来讨论一下。
goodname 2009-05-22
  • 打赏
  • 举报
回复
你这个程序实在是太长了,能否简化一下,把用不到的函数都删掉。
superflytiger 2009-05-22
  • 打赏
  • 举报
回复
源代码好像上传不成功,感兴趣的可以留下邮箱,我会尽快传过去的。(VS2005编译环境)

65,210

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧