首页 新闻 会员 周边 捐助

关于C++通用模板库中的不安全操作

0
[待解决问题]

在通用模板库中有一个make_pair方法,该方法返回pair对象。代码如下:

template<class _Ty1,
class _Ty2> inline
pair<typename _Unrefwrap<_Ty1>::type,
typename _Unrefwrap<_Ty2>::type>
make_pair(_Ty1&& _Val1, _Ty2&& _Val2)
{
typedef pair<typename _Unrefwrap<_Ty1>::type, typename _Unrefwrap<_Ty2>::type> _Mypair; return (_Mypair(_STD forward<_Ty1>(_Val1), _STD forward<_Ty2>(_Val2))); }

该该方法的原理return pair(构造函数)。仿造写了一个验证方法,发现了这个方法存在不安全访问的情况。

 1 class TestClass
 2 {
 3  4 public:
 5 int id;
 6 string name;
 7 TestClass()
 8 {
 9 cout<<" create TestClass\n";
10 name="NameValue";
11 12 }
13 TestClass(int aid)
14 {id=aid;
15 cout<<" create TestClass \n";
16 name="NameValue";
17 18 }
19 ~TestClass()
20 {
21 name="";
22 cout<<this<<":"<<id<<" TestClass Free \n";
23 24 }
25 };
26 inline TestClass GetTestClass(int aid) 
27 {
28 return TestClass(aid);
29 };
30 int main()
31 {
32 TestClass *a;
33 34 a=&(GetTestClass(1));
35 cout<<a<<":"<<a->id<<" testclass in a \n";
36 cout<<a<<":a->name:"<<a->name<<endl;
37 }

输出:

create TestClass
001AF5E4:1 TestClass Free 
001AF5E4:1 testclass in a
001AF5E4:a->name:

该输出说明,在return的时候,对象已经被释放掉了,用声明的方式创建的对象不能被传递到方法之外。虽然指针a指向内存空间依然可以找到id的值,但是name的值在析构的时候已经被销毁了。所以,模板库中的make_pair函数应该是不安全的。

孤灯下的主页 孤灯下 | 菜鸟二级 | 园豆:202
提问于:2012-04-11 18:03
< >
分享
所有回答(1)
0

提出疑问是好的,不过首先要做好功课。

出现不安全因素是因为你使用的问题:

GetTestClass(1)会产生一个临时对象,临时对象的作用域:
1. 在单一语句执行结束之后销毁,所以你看到了:
TestClass Free。
2.by value或reference-to-const隐式类别转换, 对象在函数返回前销毁

而我们在使用make_pair的时候或者是m.insert(make_pair(k, v)),或者是
pair<uint32, uint16> p = some function calls make_pair,前者是传递引用,函数返回前销毁,后者值拷贝。

不会像你这样使用:a=&(GetTestClass(1));
zsounder | 园豆:2819 (老鸟四级) | 2012-04-12 09:30

谢谢Wang Hu的回答。你给出的答案我基本上明白了,我之所以采用

a=&(GetTestClass(1));的原因是 如果我定义TestClass a=GetTestClass(1);就需要定义TestClas的拷贝构造函数。在这种情况下 执行到TestClass a的时候,a对象实际上已经被初始化了一次,这就相当于额外的对象创建开销。如何解决既不产生额外对象开销,直接传递数值的方式。现在C++中已经将struct也变成了Class的变种,除了用引用类型参数,还真没办法将对象在低损耗的情况下从函数里面传递出来。
支持(0) 反对(0) 孤灯下 | 园豆:202 (菜鸟二级) | 2012-04-12 15:02

目前在C++中除了原始类型,很难找到一个复杂的值类型进行传递。即使是传递,也需要额外的开销。C++的效率保证,很蛋疼啊。

支持(0) 反对(0) 孤灯下 | 园豆:202 (菜鸟二级) | 2012-04-12 15:04

谢谢wang Hui,我验证了一下,TestClass a=GetClass(1){return TestClass(1);} 没有调用拷贝构造函数,系统进行深度复制了吗?

支持(0) 反对(0) 孤灯下 | 园豆:202 (菜鸟二级) | 2012-04-12 15:50

@孤灯下: 你应该同时写好operator=和拷贝构造函数,不要只写一个,拷贝构造函数是复制,原来有值,进行更改,而operator是赋值,原来没有值,现在设定值,所以这里会调用operator=而不是拷贝构造函数。

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-04-13 02:25

@Wang Hui: 我已经验证清楚了这个问题。其实拷贝构造函数会被基类提供的=操作符自动调用的。后来改写了程序。可以搞定这个问题了。在这种操作方面,托管语言的操作效率高于C++。C++传递指针效率高于托管语言。托管如果 return object 的话。传递的是被托管的指针,对象没有被复制。C++的传递地址还是传值,需要程序员来控制,当然也提供了控制手段,而在托管语言中完全依赖于类型自身。

不过针对这个问题,采用声明方式初始化的对象安全程度要高于new。但是效率低于new建立的对象。如果new运算符能够提供在指定内存块建立对象的方法就好了。

支持(0) 反对(0) 孤灯下 | 园豆:202 (菜鸟二级) | 2012-04-14 21:46

@孤灯下: new有提供这种功能的,也就是placement new,你可以搜索一下,placement new就是在提供的内存区域上创建对象。

支持(0) 反对(0) zsounder | 园豆:2819 (老鸟四级) | 2012-04-14 22:18
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册