在通用模板库中有一个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函数应该是不安全的。
提出疑问是好的,不过首先要做好功课。
出现不安全因素是因为你使用的问题:
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));
谢谢Wang Hu的回答。你给出的答案我基本上明白了,我之所以采用
a=&(GetTestClass(1));的原因是 如果我定义TestClass a=GetTestClass(1);就需要定义TestClas的拷贝构造函数。在这种情况下 执行到TestClass a的时候,a对象实际上已经被初始化了一次,这就相当于额外的对象创建开销。如何解决既不产生额外对象开销,直接传递数值的方式。现在C++中已经将struct也变成了Class的变种,除了用引用类型参数,还真没办法将对象在低损耗的情况下从函数里面传递出来。
目前在C++中除了原始类型,很难找到一个复杂的值类型进行传递。即使是传递,也需要额外的开销。C++的效率保证,很蛋疼啊。
谢谢wang Hui,我验证了一下,TestClass a=GetClass(1){return TestClass(1);} 没有调用拷贝构造函数,系统进行深度复制了吗?
@孤灯下: 你应该同时写好operator=和拷贝构造函数,不要只写一个,拷贝构造函数是复制,原来有值,进行更改,而operator是赋值,原来没有值,现在设定值,所以这里会调用operator=而不是拷贝构造函数。
@Wang Hui: 我已经验证清楚了这个问题。其实拷贝构造函数会被基类提供的=操作符自动调用的。后来改写了程序。可以搞定这个问题了。在这种操作方面,托管语言的操作效率高于C++。C++传递指针效率高于托管语言。托管如果 return object 的话。传递的是被托管的指针,对象没有被复制。C++的传递地址还是传值,需要程序员来控制,当然也提供了控制手段,而在托管语言中完全依赖于类型自身。
不过针对这个问题,采用声明方式初始化的对象安全程度要高于new。但是效率低于new建立的对象。如果new运算符能够提供在指定内存块建立对象的方法就好了。
@孤灯下: new有提供这种功能的,也就是placement new,你可以搜索一下,placement new就是在提供的内存区域上创建对象。