请问达人一个关于调用dll与dll中new和delete的有关问题
请教达人一个关于调用dll与dll中new和delete的问题
大环境:
dll中有一导出类A, 在一程序中调用此dll, 并在程序中如此: A* pa = new A;
在dll中有一个接口 SetA(A* pa);可以把A的指针传进dll中.最后在dll中释放pa这个指针
<PS:达人们不用说这样做很笨,完全错误,请先看完,谢谢,仅仅是一个讨论>
有3种情况:
1. A不从任何其他类派生,最后在dll中delete pa; 众所周知,如此应该算是错误的。
2。A从一个Ref类派生出来 <Ref类的说明:即一个计数器类,有AddRef,Release两个函数,Release函数实现delete this功能> 。 最后在dll中pa-> Release();
3。A从Ref类派生,在上面说的程序中有一类B派生自A,并如此:A* pb = new B;然后同样通过SetA把pb的指针传给dll,最后在dll中 pb-> Release();
请问后面这两种情况是对?是错?对在那里?错在那里?
------解决方案--------------------
还是不要dll导出类最天下太平。
如果一定要传类对象指针,那么谁new的就谁delete。
其它情况,我是搞不定,也不想搞。
------解决方案--------------------
1 错误,肯定不行
2 在那里创建呢?
3 也不可以
在dll里面创建对象,在dll里面销毁.
要是真想用 写COM吧
------解决方案--------------------
之所以1是错误的, 因为在没有规范的回收手段时, 哪里new就要在哪里delete.
2我认为是正确的, 因为add和release提供了一种回收手段. 这个有点象COM.
3感觉和2是一个道理吧, 而且有点乱.
------解决方案--------------------
三种情况是一个问题,就是从堆中产生的对象,分配一次内存,需释放有且仅有一次。
第一种情况的错误在于没有明确pa由谁释放的问题。出现错误的情况是主程序和dll都delete一次pa。谁产生谁释放是一种好的编程方法,但不是唯一正确的答案。如果dll的设计者和使用者有所约定:凡是通过SetA传递的对象均由dll使用者产生,并由dll负责释放有且仅有一次,那么就不会出错了。
第二、三种情况是一样的,问题不在于做法本身,而是有没有明确责任。AddRef和Release只是通过接口帮助双方明确了责任所在,并代为引用计数。由使用者调用new(从而AddRef),再由dll调用Release(从而delete),只是将new-delete的一一匹配向用户透明了,并不比一方调new,一方调delete更正确。
所以,任何时候,不管类设计得如何花哨,只要判断程序中一个堆中产生的对象确实发生了正好一次释放内存的动作,那么就是正确的;否则就是错误的。
------解决方案--------------------
跨DLL(跨模块)的new ,delete是有问题的。解决办法:
dll中导出全局函数
extern "C " _declspec(dllexport) A * stdcall CreateA()
{
return new A();
}
再在A中新增一方法:
class A
{
void Delete()
{
delete this;
};
};
这样不管在什么模块中,都可以通过CreateA返回对象,delete删除对象,都没有问题。
------解决方案--------------------
楼主的意思我明白了。就是在A中申明一虚函数
class A
{
virtual void _stdcall delete()
{
delete this;
};
};
dll中导出全局函数
extern "C " _declspec(dllexport) A * stdcall CreateA()
{
return new A();
};
如果用户直接用A类,则调用CreateA() 和对应的Delete,则new, 和Delete都在Dll内,没有问题。
如果用户派生了类B,则重在Delete函数
则Class B : public A
{
virtual void _stdcall delete()
{
delete this;
};
}.
则用户使用 new B() 和对象的B-> Delete() ,则new 和 Delete都不在Dll模块中,则同样也没有问题。
------解决方案--------------------
在哪个Module里面分配内存,就应该在哪个Module里面释放,否则结果无法预测。即使是像Vitin说的那样保证一次,也不行。即使现在不出事情,早晚也很危险。
原因是这样的,new的时候需要在Heap上分配内存,大家也知道,一个进程可以有很多Heap,你如果从Heap A new, 然后到Heap B去Release,立即造成Heap Corruption,如果程序立即Crash还好,如果不Crash,累死你也不明白以后一个无辜的new 为啥 Crash了。
至于new 和 release用哪个Heap完全看你如何Link Runtime了,和Link哪一版的Runtime,你可以选择动态连接,静态连接,一个Debug Version,一个Release Version,一个连接6。0版的msvcrt.dll,一个连接8。0版的,会成为维护的恶梦。即使你能保证.exe和.dll一定都是动态连接一个版本的Runtime,你的聪明同事哪天来了自己的Memory Pool,把new给overload了,他想不到你的delete是在海的那一边。
你可以做个简单的试验,把你的.exe做Release Build, .dll做Debug Build,然后执行,等着Heap Corruption就行了,如果不发生我送你100分。:)
从理论上来说,任何资源的分配方都应该是资源的回收方,COM就是基于这个原则的。
大环境:
dll中有一导出类A, 在一程序中调用此dll, 并在程序中如此: A* pa = new A;
在dll中有一个接口 SetA(A* pa);可以把A的指针传进dll中.最后在dll中释放pa这个指针
<PS:达人们不用说这样做很笨,完全错误,请先看完,谢谢,仅仅是一个讨论>
有3种情况:
1. A不从任何其他类派生,最后在dll中delete pa; 众所周知,如此应该算是错误的。
2。A从一个Ref类派生出来 <Ref类的说明:即一个计数器类,有AddRef,Release两个函数,Release函数实现delete this功能> 。 最后在dll中pa-> Release();
3。A从Ref类派生,在上面说的程序中有一类B派生自A,并如此:A* pb = new B;然后同样通过SetA把pb的指针传给dll,最后在dll中 pb-> Release();
请问后面这两种情况是对?是错?对在那里?错在那里?
------解决方案--------------------
还是不要dll导出类最天下太平。
如果一定要传类对象指针,那么谁new的就谁delete。
其它情况,我是搞不定,也不想搞。
------解决方案--------------------
1 错误,肯定不行
2 在那里创建呢?
3 也不可以
在dll里面创建对象,在dll里面销毁.
要是真想用 写COM吧
------解决方案--------------------
之所以1是错误的, 因为在没有规范的回收手段时, 哪里new就要在哪里delete.
2我认为是正确的, 因为add和release提供了一种回收手段. 这个有点象COM.
3感觉和2是一个道理吧, 而且有点乱.
------解决方案--------------------
三种情况是一个问题,就是从堆中产生的对象,分配一次内存,需释放有且仅有一次。
第一种情况的错误在于没有明确pa由谁释放的问题。出现错误的情况是主程序和dll都delete一次pa。谁产生谁释放是一种好的编程方法,但不是唯一正确的答案。如果dll的设计者和使用者有所约定:凡是通过SetA传递的对象均由dll使用者产生,并由dll负责释放有且仅有一次,那么就不会出错了。
第二、三种情况是一样的,问题不在于做法本身,而是有没有明确责任。AddRef和Release只是通过接口帮助双方明确了责任所在,并代为引用计数。由使用者调用new(从而AddRef),再由dll调用Release(从而delete),只是将new-delete的一一匹配向用户透明了,并不比一方调new,一方调delete更正确。
所以,任何时候,不管类设计得如何花哨,只要判断程序中一个堆中产生的对象确实发生了正好一次释放内存的动作,那么就是正确的;否则就是错误的。
------解决方案--------------------
跨DLL(跨模块)的new ,delete是有问题的。解决办法:
dll中导出全局函数
extern "C " _declspec(dllexport) A * stdcall CreateA()
{
return new A();
}
再在A中新增一方法:
class A
{
void Delete()
{
delete this;
};
};
这样不管在什么模块中,都可以通过CreateA返回对象,delete删除对象,都没有问题。
------解决方案--------------------
楼主的意思我明白了。就是在A中申明一虚函数
class A
{
virtual void _stdcall delete()
{
delete this;
};
};
dll中导出全局函数
extern "C " _declspec(dllexport) A * stdcall CreateA()
{
return new A();
};
如果用户直接用A类,则调用CreateA() 和对应的Delete,则new, 和Delete都在Dll内,没有问题。
如果用户派生了类B,则重在Delete函数
则Class B : public A
{
virtual void _stdcall delete()
{
delete this;
};
}.
则用户使用 new B() 和对象的B-> Delete() ,则new 和 Delete都不在Dll模块中,则同样也没有问题。
------解决方案--------------------
在哪个Module里面分配内存,就应该在哪个Module里面释放,否则结果无法预测。即使是像Vitin说的那样保证一次,也不行。即使现在不出事情,早晚也很危险。
原因是这样的,new的时候需要在Heap上分配内存,大家也知道,一个进程可以有很多Heap,你如果从Heap A new, 然后到Heap B去Release,立即造成Heap Corruption,如果程序立即Crash还好,如果不Crash,累死你也不明白以后一个无辜的new 为啥 Crash了。
至于new 和 release用哪个Heap完全看你如何Link Runtime了,和Link哪一版的Runtime,你可以选择动态连接,静态连接,一个Debug Version,一个Release Version,一个连接6。0版的msvcrt.dll,一个连接8。0版的,会成为维护的恶梦。即使你能保证.exe和.dll一定都是动态连接一个版本的Runtime,你的聪明同事哪天来了自己的Memory Pool,把new给overload了,他想不到你的delete是在海的那一边。
你可以做个简单的试验,把你的.exe做Release Build, .dll做Debug Build,然后执行,等着Heap Corruption就行了,如果不发生我送你100分。:)
从理论上来说,任何资源的分配方都应该是资源的回收方,COM就是基于这个原则的。