65.9K
CodeProject 正在变化。 阅读更多。
Home

托管 c++ 中的 Bug

starIcon
emptyStarIcon
starIcon
emptyStarIconemptyStarIconemptyStarIcon

1.57/5 (6投票s)

2004年8月27日

CPOL

1分钟阅读

viewsIcon

36168

看起来是在托管 C++ 中,使用非托管模板类/结构时出现的错误。

引言

看起来在托管 C++ 中,调用带有模板参数的虚成员函数时会出现错误。

示例

#include "stdafx.h"
#using <mscorlib.dll>
#include <tchar.h>

using namespace System;

template <class T>
struct xroot
{
    xroot() {}
    xroot(T t) {}
    xroot(const xroot &x) {}
    ~xroot() {}
};

class CClass
{
public:
    virtual void func(xroot<int> x) {}
};

/* 
void fff() {
    xroot<int> _a;
    xroot<int> _b(_a);
}
*/

int _tmain(void)
{
    CClass *c = new CClass();
    c->func(5);
    return 0;

}

 

注释

根据 C++ 规范,代码中的调用顺序应该是

c->func(5);

如下:
1. 调用构造函数 xroot<int>(int t)
2. 调用 c->func,并将刚构造的参数传递给它
3. 调用参数的析构函数
4. 从 c->func 返回

但在托管 C++ 项目中,顺序看起来像
1. 调用构造函数 xroot<int>(int t)
2. 调用刚构造的 xroot 的析构函数
3. 调用 c->func,并将刚构造的参数传递给它
4. 再次调用参数的析构函数
5. 从 c->func 返回

但实际上,对同一个 xroot 没有两次析构函数调用——而是存在第二个 xroot,它是由默认拷贝构造函数构造的。是的,我们有自己的拷贝构造函数,但由于编译器错误,它没有被实例化。相反,编译器实例化了默认构造函数。要检查这一点,请取消注释 fff() 函数——现在拷贝构造函数将被实例化,调用顺序将如下:
1. 调用构造函数 xroot<int>(int t)
2. 调用拷贝构造函数 xroot<int>(const xroot<int> &x),并将刚构造的 xroot 传递给它
3. 调用第一个 xroot 的析构函数
4. 调用 c->func,并将刚构造的参数传递给它
5. 调用第二个 xroot 的析构函数
6. 从 c->func 返回

这里有一些非常有趣的事情——这个问题仅在以下情况下存在:
1. 在托管 C++ 项目中(非托管 C++ .NET 运行良好)
2. 在模板类/结构中
3. 模板的构造函数将构造的参数作为虚成员函数的参数传递
4. 没有其他代码可以强制编译器实例化拷贝构造函数

结论

因此,实际上我们在托管 C++ 编译器中存在两个错误。
第一个错误——没有必要通过调用拷贝构造函数来创建参数的第二个实例——根据规范,应该将第一个实例发送到被调用的函数。
第二个错误——编译器没有实例化拷贝构造函数,而是使用了默认构造函数。

 

© . All rights reserved.