指针不允许两次删除?

 

 

#pragma warning (disable : 4786)
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;

class A
{
public:
 ~A() { }
 void pOut()
 {
  doOut();
 }
 virtual void doOut() = 0;

};

class BA: public A
{
public:
 ~BA() { }
 virtual void doOut()
 {
  cout << m_i << endl;
 }
 int m_i;
};

class CA: public A
{
public:
 string m_str;
 ~CA() {}
 virtual void doOut()
 {
  cout << m_str << endl;
 }
};

class DA : public A
{
public:
 ~DA();

 void clear();
 vector<A *> m_vec;
 virtual void doOut();
};


class EA : public A
{
public:
 
 virtual void doOut();
 void clear();

 ~EA()
 {
  clear();
 }
 map<string, A*> m_map;
};

 

void DA::doOut()
{
 vector<A *>::iterator it;
 for (it = m_vec.begin(); it != m_vec.end(); it++)
 {
  (*it)->doOut();
 }
}

void EA::doOut()
{
 map<string, A*> ::iterator it;
 for (it = m_map.begin(); it != m_map.end(); it++)
 {
  cout << (*it).first << " ===> " << endl;
  (*it).second->doOut();
 }
}

void EA ::clear()
{
 map<string, A*> ::iterator it;
 for (it = m_map.begin(); it != m_map.end(); it++)
 {
  cout << (*it).first << " ===> " << endl;
  (*it).second->doOut();
  delete (*it).second;
 }
}
DA::~DA()
{
 clear();
}
void DA::clear()
{
 vector<A *> ::iterator it = m_vec.begin();
 for (it; it != m_vec.end(); it++)
 {
  delete (*it);
 }
 m_vec.clear();
}

/*
void foo(A *pa)
{
 BA *pca1 = static_cast<BA *> ( pa );
 cout << "in function foo(), pca1->doOut() " << endl;
 pca1->doOut();

 BA *pca2 = dynamic_cast<BA *> ( pa );
 cout << "in function foo(), pca2->doOut() " << endl;
 pca2->doOut();

}
*/
int main()
{
 BA *pBa = new BA;
 pBa->m_i = 100;

 CA *pCa = new CA;
 pCa->m_str = "myprogram";

 DA da;
 da.m_vec.push_back(pBa);
 da.m_vec.push_back(pCa);

// foo(pa1);

 //请问一下,我要在这里输出da.m_vec的所有内容,应该如何实现呢?
 //da.doOut();
 
 DA *pDa2 = new DA;
 pDa2->m_vec.push_back(pBa);
 pDa2->m_vec.push_back(pCa);
 pDa2->doOut();
 delete pDa2;

  

 return 0;
}

 

上面这段程序执行后会出错,

因为delete pDa2时,已经把pBa, pCa给删除了,所以,而在return 0;执行之前,会调用da的析构函数,而在此之前,调用pDa2的析构函数的时候,已经把pBa,pCa给释放掉了,所以,调用da的析构函数时,再次释放pBa, pCa所指的内存空间会出错。

 

 

见下帖:

对一个指针进行两次delete操作,会出现什么结果?

楼主BlueprintIrene(我只能用程序来麻痹自己) 2004-07-06 16:03:12 在 C/C++ / C++ 语言 提问

//   有如下程序:  
  class   Test  
  {  
  public:  
          Test()   :   m_id(   s_count++   )    
          {  
                  cout   <<   "created:   "   <<   m_id   <<   endl;  
          }  
          ~Test()  
          {  
                  cout   <<   "destroyed:   "   <<   m_id   <<   endl;  
          }  
   
  private:  
          static   in   s_count;  
          int   m_id;  
  };  
   
  int   Test::s_count   =   0;  
   
  void   main()  
  {  
          Test   *pTest   =   new   Test[3];  
          delete   pTest;  
          delete   pTest;  
  }  
   
  /*  
  程序输出结果:  
  created:   0  
  created:   1  
  created:   2  
  destroyed:   0  
  destroyed:   0  
  */  
   
  -----------------------------------------------------------  
   
  1.为什么可以对指针p进行两次delete操作?为什么这里运行好象并没有出现问题,并且两次操作有同样的输出结果?  
   
  2.当我把main()函数改写为  
  void   main()  
  {  
          Test   *pTest   =   new   Test[3];  
          delete   []   pTest;  
  }  
  后,程序输出的结果为  
  created:   0  
  created:   1  
  created:   2  
  destroyed:   2  
  destroyed:   1  
  destroyed:   0  
  那为什么原先的那个程序的最后两行输出结果为  
  destroyed:   0  
  destroyed:   0  
  却不是  
  destroyed:   2  
  destroyed:   2  
  ?  
   
  注意:这个程序本身就有问题,运行会造成内存泄漏,所以这里我想讨论的不是程序的安全性问题,而是关注于其输出结果,为什么会有这样的输出结果呢?  
  另外,我使用的是VC6   +   Windows   XP。  
   
   
  问题点数:50、回复次数:21Top

1 楼gorge_an(木头)回复于 2004-07-06 16:07:40 得分 0

学好C语言Top

2 楼yjh1982(血精灵)回复于 2004-07-06 16:08:49 得分 0

什么结果都有:)Top

3 楼dingfuhao(丁丁)回复于 2004-07-06 16:37:15 得分 10

这个问题是和编译器有一定的关系  
  在VC中,Debug模式下如果你释放内存,编译器会将释放内存的内容填充成0xEEFE  
  当你再次释放的时候会有断言错误.而在Release模式下,释放内存后,内存的内容并  
  不改变,所以你两次调用可以得到相同的结果  
   
  为什么是destroyed:   0,   是因为你释放pTest   =   pTest[0];  
  Top

4 楼xjp6688(大平/要做必须最好)回复于 2004-07-06 16:41:04 得分 0

什么是DEBUG模式,什么是RELEASE模式?Top

5 楼peter9606( 荷尔蒙一失效 人就清醒了)回复于 2004-07-06 16:49:09 得分 0

Test   *pTest   =   new   Test[3];  
          delete   pTest;  
          delete   pTest;  
   
  运行没有错误么?  
  Top

6 楼jwwu(开始懂了)回复于 2004-07-06 16:50:33 得分 10

和编译器有很大的关系  
  有时候  
  char   *p   =   char[100];  
  sprintf(p,   "hello   the   world/n");  
  cout   <<   p   <<   endl;  
  delete   []p;  
  cout   <<   p   <<   endl;  
  还能出结果    
   
  最好的方式是  
  delete   []p;之后p   =   (char   *)0;  
   
  Top

7 楼peter9606( 荷尔蒙一失效 人就清醒了)回复于 2004-07-06 17:08:49 得分 0

同意     jwwu(永不放弃)Top

8 楼BlueprintIrene(我只能用程序来麻痹自己)回复于 2004-07-06 17:12:10 得分 0

To   dingfuhao(丁丁):  
  是不是可以这样认为:  
  delete   pTest;   //   pTest   =   &pTest[0],你好象有笔误  
  释放的是数组第一个元素,  
  而  
  delete   []   pTest;  
  却是从数组尾部开始释放内存的?  
   
  Top

9 楼dingfuhao(丁丁)回复于 2004-07-06 17:23:32 得分 10

delete   pTest调用的是operator   delete函数,   只调用了pTest->~Test()函数,   故只  
  显示destory   0  
   
  而delete[]   pTest调用的是operator   delete[]函数,   operator   delete[]   实现对应于operator   new   []函数,   基于先创建的对象后销毁的原则,   创建的顺序是0->1->2,  
  而销毁的顺序时2->1->0Top

10 楼peter9606( 荷尔蒙一失效 人就清醒了)回复于 2004-07-06 17:26:11 得分 0

To   楼主:  
   
  如果你这样定义   :   int*   p   =   new   int[10]   ;  
  那么你就这样删除:   delete   []p   ;  
   
  千万不要问为什么不可一   delete   p   ;   因为这样是错误的。Top

11 楼BlueprintIrene(我只能用程序来麻痹自己)回复于 2004-07-06 17:31:33 得分 0

To   peter9606:  
  首先,谢谢你的回复  
  但我在这里,并不是想讨论程序的设计错误之处,而是关注于其输出结果。  
  关于这一点,我已经在提问的注意中声明过了  
   
  呵呵,可能我有点钻牛角尖了吧Top

12 楼BlueprintIrene(我只能用程序来麻痹自己)回复于 2004-07-06 17:32:55 得分 0

谢谢dingfuhao(丁丁)!Top

13 楼ricky460(阿酷)回复于 2004-07-06 17:36:09 得分 0

实际上   ,   你的编译模式设置好的话   ,   是不应该出现delete   两次指针还能没有错的   .   .   .  
   
  另外   ,   delete   pTest   ,   这句话的意思是告诉系统   ,   删除一个指针   .   .   .   ,   于是系统从pTest位置处开始寻找   ,   此时pTest指向的是pTest[0],也就是说他释放的是数组第一个元素   .  
  而   delete   []   pTest   ,   告诉系统的是要删除一个数组     .   .   .Top

14 楼darkstar21cn(≮天残≯无畏)(死亡进行时)回复于 2004-07-06 18:28:30 得分 10

有些编译器可能能够提示这种“警告”,实行其间是不会有异常的,因为delete在设计的时候没有抛出异常。  
  主要原因是指针数组,例如:  
  delete[]   p;如果delete抛出异常那么后面的指针将得不到销毁,这就造成了更为严重的内存泄漏Top

15 楼darkstar21cn(≮天残≯无畏)(死亡进行时)回复于 2004-07-06 18:33:11 得分 0

那为什么原先的那个程序的最后两行输出结果为  
  destroyed:   0  
  destroyed:   0  
  却不是  
  destroyed:   2  
  destroyed:   2  
   
  因为*ptest   指向的是第一个实例就是ptest[0];ptest[0].m_id   ==   0,OK?Top

16 楼fangrk(加把油,伙计!)回复于 2004-07-06 19:09:22 得分 0

未定义Top

17 楼peter9606( 荷尔蒙一失效 人就清醒了)回复于 2004-07-06 19:14:57 得分 0

嗯。。。  
  未定义  
  最恰当的解释了  
  Top

18 楼kahn(麟)回复于 2004-07-06 19:37:32 得分 10

听说如果不巧释放那个指针先前指向的那个区域在第一次释放后被系统信息覆盖了,那你在第二次释放后会造成系统崩溃Top

19 楼dayn9(无知者无罪)回复于 2004-07-06 21:51:38 得分 0

同意   fangrk(加把油,伙计!),重复释放,是最基本的未定义行为。每人能对其结果做出保证。  
   
  同意   kahn(麟),可能的崩溃不是最严重的后果,最严重的是可能导致缓存溢出,恭喜你,你有望被黑了。Top

20 楼eagle758(天偌)回复于 2004-07-07 10:27:27 得分 0

同意!!!!!darkstar21cn(暗星)Top

21 楼rorot(rorot)回复于 2004-07-07 13:07:04 得分 0

使用auto_ptr试试.....

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<think>我们正在处理的数据结构是:std::list<std::pair<std::string, std::shared_ptr<T>>> 用户的问题有两个部分: 1. 该数据结构是否允许字符串键重复? 2. 如何从该数据结构中删除元素? 首先,关于键重复问题: - std::list 是一个双向链表,它可以存储重复的元素。因此,在这个list中,可以存在多个具有相同字符串(即键)的pair。也就是说,这个数据结构允许键重复。 其,关于删除元素: 由于list允许重复,所以删除操作需要明确是删除一个匹配的元素还是所有匹配的元素。同时,因为元素是pair,我们可能需要根据字符串键(first)来删除,也可能需要根据整个pair的内容(包括共享指针指向的对象)来删除删除元素的方法: 1. 使用成员函数 remove_if:可以指定一个条件(谓词)来删除所有满足条件的元素。 2. 使用迭代器遍历,配合 erase 函数删除特定位置的元素(可以删除单个元素)。 注意:在遍历过程中删除元素时,需要注意迭代器失效的问题。对于std::list,使用erase删除当前元素会使当前迭代器失效,但返回下一个有效的迭代器,因此可以使用erase的返回值来继续遍历。 下面分别给出几种删除场景的示例: 场景1:删除所有键(first)等于特定字符串的元素 场景2删除键(first)等于特定字符串且值(second,即shared_ptr指向的对象)满足特定条件的元素 场景3:删除单个匹配的元素(例如第一出现的) 我们使用lambda表达式作为谓词,可以方便地编写条件。 示例代码: 假设我们有一个list: std::list<std::pair<std::string, std::shared_ptr<int>>> dataList; 场景1:删除所有键为"key1"的元素 ```cpp dataList.remove_if([](const auto& pair) { return pair.first == "key1"; }); ``` 场景2删除键为"key1"且指向的int值大于100的元素 ```cpp dataList.remove_if([](const auto& pair) { return (pair.first == "key1") && (*pair.second > 100); }); ``` 场景3:删除一个键为"key1"的元素(使用迭代器) ```cpp for (auto it = dataList.begin(); it != dataList.end(); ) { if (it->first == "key1") { it = dataList.erase(it); // erase返回下一个元素的迭代器 break; // 只删除一个,所以找到后删除并退出循环 } else { ++it; } } ``` 注意:如果使用迭代器删除立即跳出循环,则会删除所有匹配的元素。上面的代码在删除一个匹配的元素后跳出循环。 另外,由于list的erase操作会返回下一个迭代器,所以我们在删除元素后应该使用返回值更新it,否则it会失效。 关于共享指针的注意事项: 当我们从list中删除一个pair时,该pair中的shared_ptr会被销毁,因此其引用计数会减少。如果引用计数变为0,则指向的对象会被删除。这符合预期。 总结: 1. std::list允许重复的键(因为list要求元素唯一)。 2. 删除元素可以使用remove_if(一删除所有满足条件的)或迭代器erase(可以控制删除的个数)。 引用说明: 在删除元素时,我们使用了erase方法,这与引用[1]中map的erase类似,但list的erase没有返回移除元素的个数(它返回的是迭代器),而remove_if会移除所有满足条件的元素。引用[1]提到map的erase返回移除个数,但list的remove_if没有返回值(返回void),而erase单个元素返回的是迭代器。另外,引用[2]和引用[3]是关于map和list迭代器类型的错误,与当前问题关系大,但我们可以知道list的迭代器类型有const和非const之分,在非const的list上可以使用非const迭代器进行删除操作。 因此,在回答中,我们主要参考了引用[1]中erase的使用思想,但具体实现因容器同而同。 下面给出一个完整的示例: ```cpp #include <list> #include <memory> #include <string> #include <iostream> int main() { // 创建list std::list<std::pair<std::string, std::shared_ptr<int>>> dataList; // 添加一些元素 dataList.push_back(std::make_pair("key1", std::make_shared<int>(10))); dataList.push_back(std::make_pair("key2", std::make_shared<int>(20))); dataList.push_back(std::make_pair("key1", std::make_shared<int>(30))); // 重复的key1 dataList.push_back(std::make_pair("key1", std::make_shared<int>(150))); // 重复的key1,且值大于100 // 场景1:删除所有键为"key1"的元素 // dataList.remove_if([](const auto& pair) { return pair.first == "key1"; }); // 场景2删除键为"key1"且值大于100的元素 // dataList.remove_if([](const auto& pair) { return pair.first == "key1" && *pair.second > 100; }); // 场景3:删除一个键为"key1"的元素 for (auto it = dataList.begin(); it != dataList.end(); ) { if (it->first == "key1") { it = dataList.erase(it); break; } else { ++it; } } // 打印剩余元素 for (const auto& [key, ptr] : dataList) { std::cout << "Key: " << key << ", Value: " << *ptr << std::endl; } return 0; } ``` 运行场景3后,输出: Key: key2, Value: 20 Key: key1, Value: 30 Key: key1, Value: 150 因为只删除了第一个"key1"。 如果运行场景1,则输出: Key: key2, Value: 20 如果运行场景2,则输出: Key: key1, Value: 10 Key: key2, Value: 20 Key: key1, Value: 30 因为删除了"key1"中值为150的元素(大于100)。 因此,根据需求选择合适的删除方式。 相关问题: 1. 如何在遍历std::list时安全删除多个元素? 2. 在std::list中存储shared_ptr,如何避免循环引用导致的内存泄漏? 3. 比较std::list和std::vector在频繁删除插入操作中的性能差异。 4. 如何利用智能指针管理动态分配的对象,并存储在容器中?</think># 从 `std::list<std::pair<std::string, std::shared_ptr<T>>>` 中删除元素 ## 键重复性 **该数据结构允许键重复**。`std::list` 是一个双向链表容器,它要求元素具有唯一键。您可以存储多个具有相同字符串键的 `pair` 元素。例如: ```cpp std::list<std::pair<std::string, std::shared_ptr<int>>> dataList; dataList.push_back({"A", std::make_shared<int>(10)}); dataList.push_back({"A", std::make_shared<int>(20)}); // 允许键重复 dataList.push_back({"B", std::make_shared<int>(30)}); ``` ## 删除元素的三种方法 ### 1. 使用 `remove_if` 删除所有匹配元素(推荐) ```cpp // 删除所有键为"targetKey"的元素 std::string targetKey = "A"; dataList.remove_if([&](const auto& pair) { return pair.first == targetKey; }); // 删除键为"targetKey"且值大于15的元素 dataList.remove_if([&](const auto& pair) { return pair.first == targetKey && *pair.second > 15; }); ``` ### 2. 使用迭代器删除特定元素 ```cpp for (auto it = dataList.begin(); it != dataList.end(); ) { if (it->first == targetKey) { // 删除当前元素并获取下一个有效迭代器 it = dataList.erase(it); } else { ++it; } } ``` ### 3. 使用 `std::find_if` 删除单个元素 ```cpp auto it = std::find_if(dataList.begin(), dataList.end(), [&](const auto& pair) { return pair.first == targetKey && *pair.second == 20; }); if (it != dataList.end()) { dataList.erase(it); } ``` ## 完整示例 ```cpp #include <iostream> #include <list> #include <memory> #include <algorithm> int main() { using DataType = std::list<std::pair<std::string, std::shared_ptr<int>>>; DataType dataList; // 添加元素(包含重复键) dataList.emplace_back("A", std::make_shared<int>(10)); dataList.emplace_back("B", std::make_shared<int>(20)); dataList.emplace_back("A", std::make_shared<int>(30)); // 重复键 dataList.emplace_back("C", std::make_shared<int>(40)); // 删除所有键为"A"的元素 dataList.remove_if([](const auto& p) { return p.first == "A"; }); // 验证删除结果 std::cout << "删除后剩余元素:\n"; for (const auto& [key, ptr] : dataList) { std::cout << key << ": " << *ptr << "\n"; } return 0; } ``` 输出: ``` 删除后剩余元素: B: 20 C: 40 ``` ## 关键注意事项 1. **智能指针管理**: - 删除元素时,关联的 `shared_ptr` 引用计数会自动减少 - 当引用计数归零时,对象会被自动销毁 2. **性能考虑**: - `std::list` 的删除操作时间复杂度为 $O(1)$(已知位置)[^1] - 查找操作需要 $O(n)$ 时间,因为需要遍历链表 3. **替代数据结构**: - 如果需要唯一键和快速查找,考虑使用 `std::map` 或 `std::unordered_map`[^2] - `std::map` 保证有序,查找时间复杂度 $O(\log n)$ - `std::unordered_map` 提供平均 $O(1)$ 的查找性能 4. **迭代器安全**: - 删除元素后,指向被删元素的迭代器会失效 - 使用 `erase()` 的返回值更新迭代器可避免问题[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值