请教一个关于std::vector深拷贝的问题?

时间:2021-10-09 22:49:58
最近再程序中用到一个vector容器包含的对象含有指针变量, 所以实现了复制构造函数来让vector可以复制指针指向的内容.  但诡异的是程序运行到一点就不往下执行了, 也没提示什么信息. 异常运行结果如图:
请教一个关于std::vector深拷贝的问题?
正常应该执行100次循环的

下面是测试程序:

struct testClass{

testClass():mNumb(1), ptr(NULL){
ptr = new int(1);
}
testClass(const testClass& other){
this->mNumb = other.mNumb;
this->ptr = new int( *(other.ptr));
}
~testClass(){
if (ptr)
{
delete ptr;
ptr = 0;
}
}

int mNumb;
int* ptr;
};

typedef std::vector<testClass> vecArr;
vecArr::iterator it;
vecArr _vecarr;


int main()  


// _vecarr.reserve(100);

for (int i = 0; i < 100; i++)
{
testClass test1;
_vecarr.push_back( test1 );
testClass test2;
_vecarr.push_back( test2);


printf("vec size: %d\n", _vecarr.size());

it = _vecarr.begin() + (_vecarr.size()/2);
 _vecarr.erase(it);

printf("vec size: %d, capacity: %d \n", _vecarr.size(), _vecarr.capacity());

}

printf("The end");
}


其他情况: 1 如果每次循环一push_back() 一次操作的话, 可以正常执行, 
          2 如果预先分配指定元素的话, 程序会执行到分配大小出中断.

请问问题会出在什么地方呢?

11 个解决方案

#1


什么编译器?

#2


testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。

#3


请添加赋值操作符重载

#4


引用 2 楼 ananluowei 的回复:
testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。

正解:
你需要重载=运算符:
testClass&operator=(const testClass& other)
{
    mNumb = other.mNumb;
    ptr = new int(*(other.ptr)) ;
    return *this;
}
vector在添加删除元素的时候会产生复制操作,会调用operator=, 如果你没有重载,编译器默认的操作是内存拷贝,你的深度复制就没起作用。

#5


引用 2 楼 ananluowei 的回复:
testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。


额, 真是的,非常感谢啊.  我一直在现象上找问题的原因, 却疏忽了原理的基本知识. 下次再使用新东西的时候一定会吃透在用的. 跟了下代码在调用erase时候, 针对后面的元素掉用的是赋值(=)函数, 对于内存分重新分配的时候调用的是拷贝构造函数. 
另外有点小迷茫, 为什么访问野指针没有报错,如果该内存恰好可用,就应该执行过去了 而不是阻塞在那里了啊? 

#6


引用 4 楼 mmns 的回复:
Quote: 引用 2 楼 ananluowei 的回复:

testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。

正解:
你需要重载=运算符:
testClass&operator=(const testClass& other)
{
    mNumb = other.mNumb;
    ptr = new int(*(other.ptr)) ;
    return *this;
}
vector在添加删除元素的时候会产生复制操作,会调用operator=, 如果你没有重载,编译器默认的操作是内存拷贝,你的深度复制就没起作用。


额, 真是的,非常感谢啊.  我一直在现象上找问题的原因, 却疏忽了原理的基本知识. 下次再使用新东西的时候一定会吃透在用的. 跟了下代码在调用erase时候, 针对后面的元素掉用的是赋值(=)函数, 对于内存分重新分配的时候调用的是拷贝构造函数. 
另外有点小迷茫, 为什么访问野指针没有报错,如果该内存恰好可用,就应该执行过去了 而不是阻塞在那里了啊? 

#7


需要看一下实现机制,如果涉及到顺序改变,就会调用operator=, 就有隐患,但也不一定出错。
这和stl的实现方式有关。仅就vc++带的那个stl来说,它有自己的内存管理池,你向vector添加的所有元素都是从那个池中分配的,并且释放时也不是真的释放掉,而释放回到stl自己的内存缓冲池里,因为像vector这样的结构,每次添加和删除以及挪动位置,都会频繁导致新的元素的分配释放。为了避免频繁的内存分配释放和分配造成的时间成本,stl把自己做了管理机制。这就是你说的,为啥释放了还能访问,因为对于系统来说,你的那块内存没有真正释放,还是归你应用程序所有,只是暂时由stl保管。但建议你不要使用这个变量,stl不知道啥时候就把它分配出去了。

#8


该回复于2013-09-17 14:02:26被管理员删除

#9


引用 7 楼 mmns 的回复:
需要看一下实现机制,如果涉及到顺序改变,就会调用operator=, 就有隐患,但也不一定出错。
这和stl的实现方式有关。仅就vc++带的那个stl来说,它有自己的内存管理池,你向vector添加的所有元素都是从那个池中分配的,并且释放时也不是真的释放掉,而释放回到stl自己的内存缓冲池里,因为像vector这样的结构,每次添加和删除以及挪动位置,都会频繁导致新的元素的分配释放。为了避免频繁的内存分配释放和分配造成的时间成本,stl把自己做了管理机制。这就是你说的,为啥释放了还能访问,因为对于系统来说,你的那块内存没有真正释放,还是归你应用程序所有,只是暂时由stl保管。但建议你不要使用这个变量,stl不知道啥时候就把它分配出去了。
vc++的stl有内存池?确定?
我只知道sgi的是用内存池的

#10


如果你的编译器版本够新,支持C++11的话,可能没必要重载operator=().
仅供参考

#11


6楼正解~~

#1


什么编译器?

#2


testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。

#3


请添加赋值操作符重载

#4


引用 2 楼 ananluowei 的回复:
testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。

正解:
你需要重载=运算符:
testClass&operator=(const testClass& other)
{
    mNumb = other.mNumb;
    ptr = new int(*(other.ptr)) ;
    return *this;
}
vector在添加删除元素的时候会产生复制操作,会调用operator=, 如果你没有重载,编译器默认的操作是内存拷贝,你的深度复制就没起作用。

#5


引用 2 楼 ananluowei 的回复:
testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。


额, 真是的,非常感谢啊.  我一直在现象上找问题的原因, 却疏忽了原理的基本知识. 下次再使用新东西的时候一定会吃透在用的. 跟了下代码在调用erase时候, 针对后面的元素掉用的是赋值(=)函数, 对于内存分重新分配的时候调用的是拷贝构造函数. 
另外有点小迷茫, 为什么访问野指针没有报错,如果该内存恰好可用,就应该执行过去了 而不是阻塞在那里了啊? 

#6


引用 4 楼 mmns 的回复:
Quote: 引用 2 楼 ananluowei 的回复:

testClass 只有 复制构造,没有赋值重载??
基本上要么都不写,要么都写。只写一个多半是错的。

正解:
你需要重载=运算符:
testClass&operator=(const testClass& other)
{
    mNumb = other.mNumb;
    ptr = new int(*(other.ptr)) ;
    return *this;
}
vector在添加删除元素的时候会产生复制操作,会调用operator=, 如果你没有重载,编译器默认的操作是内存拷贝,你的深度复制就没起作用。


额, 真是的,非常感谢啊.  我一直在现象上找问题的原因, 却疏忽了原理的基本知识. 下次再使用新东西的时候一定会吃透在用的. 跟了下代码在调用erase时候, 针对后面的元素掉用的是赋值(=)函数, 对于内存分重新分配的时候调用的是拷贝构造函数. 
另外有点小迷茫, 为什么访问野指针没有报错,如果该内存恰好可用,就应该执行过去了 而不是阻塞在那里了啊? 

#7


需要看一下实现机制,如果涉及到顺序改变,就会调用operator=, 就有隐患,但也不一定出错。
这和stl的实现方式有关。仅就vc++带的那个stl来说,它有自己的内存管理池,你向vector添加的所有元素都是从那个池中分配的,并且释放时也不是真的释放掉,而释放回到stl自己的内存缓冲池里,因为像vector这样的结构,每次添加和删除以及挪动位置,都会频繁导致新的元素的分配释放。为了避免频繁的内存分配释放和分配造成的时间成本,stl把自己做了管理机制。这就是你说的,为啥释放了还能访问,因为对于系统来说,你的那块内存没有真正释放,还是归你应用程序所有,只是暂时由stl保管。但建议你不要使用这个变量,stl不知道啥时候就把它分配出去了。

#8


该回复于2013-09-17 14:02:26被管理员删除

#9


引用 7 楼 mmns 的回复:
需要看一下实现机制,如果涉及到顺序改变,就会调用operator=, 就有隐患,但也不一定出错。
这和stl的实现方式有关。仅就vc++带的那个stl来说,它有自己的内存管理池,你向vector添加的所有元素都是从那个池中分配的,并且释放时也不是真的释放掉,而释放回到stl自己的内存缓冲池里,因为像vector这样的结构,每次添加和删除以及挪动位置,都会频繁导致新的元素的分配释放。为了避免频繁的内存分配释放和分配造成的时间成本,stl把自己做了管理机制。这就是你说的,为啥释放了还能访问,因为对于系统来说,你的那块内存没有真正释放,还是归你应用程序所有,只是暂时由stl保管。但建议你不要使用这个变量,stl不知道啥时候就把它分配出去了。
vc++的stl有内存池?确定?
我只知道sgi的是用内存池的

#10


如果你的编译器版本够新,支持C++11的话,可能没必要重载operator=().
仅供参考

#11


6楼正解~~