简体   繁体   English

std :: map,引用,指针和内存分配

[英]std::map, references, pointers and memory allocation

I am having a lil hard time with map and the valuetype allocation. 我正忙着使用map和valuetype分配。

consider this simple class: 考虑这个简单的类:

class Column {
private:
    char *m_Name;
public:
    // Overrides
    const char *Name(){
        return this->m_Name;
    }

    // Ctors
    Column(const char *NewName){
        this->m_Name = new char[strlen(NewName) + 1];
        strcpy(this->m_Name, NewName);
    }

    // Dtors
    ~Column(){
        cout << "wtf?\n";
        delete this->m_Name;
    }
};

now I have this map: 现在我有这张地图:

// Typedefs
typedef std::map<int, Column> ColumnContainer;
ColumnContainer *m_Container;

When i call this: 当我这样称呼:

Column *c = new Column("Test");
cout << "CREATED: " << c->Name() << "\n";
it = this->m_Container->insert(std::make_pair(0, *c)).first;
cout << "AGAIN: " << c->Name() << "\n";

the console is printing the "wtf?" 控制台正在打印“wtf?” after the insert in the map. 插入地图后。

it seems to be destroying the column. 它似乎正在摧毁这个专栏。 Is this right? 这是正确的吗?

or am I doing something wrong? 或者我做错了什么?

I was wondering if the value_type of the std::map has to a struct type with defined memory size, like with POD or array of POD? 我想知道std::mapvalue_type是否具有定义内存大小的结构类型,如POD还是POD数组?

the cout << AGAIN doesn't print the "Test" cout << AGAIN不打印“测试”

what I need is a map to a columns based on int key 我需要的是基于int键的列映射

make_pair(0, *c) creates a (temporary, unnamed) pair<int, Column> . make_pair(0, *c)创建一个(临时的,未命名的) pair<int, Column> This pair is passed to map::insert by reference, and, since std::map owns its elements, it makes a copy of the pair. 这对通过引用传递给map::insert ,并且由于std::map拥有它的元素,因此它会复制该对。 Then, the temporary pair is destroyed, destroying the Column object it contains. 然后,销毁临时对,销毁它包含的Column对象。

This is why it is necessary to properly define copy-construction for types that you want to use as standard container elements. 这就是为什么有必要为要用作标准容器元素的类型正确定义复制构造的原因。

What is happining in your code: 您的代码中有什么好处:

Here you are dynamically creating an object. 在这里,您将动态创建一个对象。 (this is not freed in your code). (这不会在您的代码中释放)。
Why are you using a pointer? 你为什么用指针? (Smart pointer are your friend.) (智能指针是你的朋友。)
But a normal object would be preferable. 但是一个普通的物体会更好。

Column *c = new Column("Test");

Now you make a copy of the object as it is copied into the temporary object of type std::pair<int,Column> (This uses Column's copy constructor which makes a copy of the M_name member (you now have two pointers to the same memory location)). 现在,您将对象的副本复制到std :: pair <int,Column>类型的临时对象中(这使用了Column的复制构造函数,该构造函数生成了M_name成员的副本(现在您有两个指向该对象的指针)记忆位置))。

std::make_pair(0, *c)

Now you insert the pair<int,Column> into the map. 现在将对<int,Column>插入到地图中。 This is done by again using the Column copy constructor (it uses the make_pair copy constructor which calls the Column copy constructor). 这是通过再次使用Column copy构造函数完成的(它使用调用Column复制构造函数的make_pair复制构造函数)。 Again the M_name pointer is copied into this object. M_name指针再次复制到此对象中。 So now you have three objects poining at the dynamically allocated string. 所以现在你有三个对象在动态分配的字符串。

m_Container->insert( pairObject )

At this point the temporary object you create with the call to std::make_pair() is no longer needed so it is destroyed. 此时,不再需要通过调用std :: make_pair()创建的临时对象,因此它将被销毁。 This is where you destructor is getting called. 这是析构函数被调用的地方。 Of course this leaves you with two objects with pointer at memory that has now been released. 当然,这会留下两个带有指针的对象,这些对象现在已经被释放了。

You have a big problem. 你有一个大问题。

You either need to use std::string (preferred solution) 您需要使用std :: string(首选解决方案)
Or you need to correctly handle the RAW owned pointer in your class. 或者您需要正确处理类中RAW拥有的指针。 This means you need to implement all four of the defaultly generated methods: 这意味着您需要实现所有四个默认生成的方法:

  • constructor 构造函数
  • destructror destructror
  • copy constructor 复制构造函数
  • assignment operator 赋值运算符

Small problem: 小问题:

You need to stop coding like a java programmer. 你需要像java程序员一样停止编码。

Column *c = new Column("Test");
it = this->m_Container->insert(std::make_pair(0, *c)).first;

Should look like this: 应该是这样的:

m_Container[0] = Column("Test");

There is no need to dynamically allocate everything. 无需动态分配所有内容。
Infact that is very dangerious. 事实上非常危险。

Explanation for why having a RAW owned pointer is a bad idea. 解释为什么拥有RAW拥有指针是一个坏主意。

class X
{
    char*   m_name;
  public:
    X(char const* name)  {m_name new char[strlen(m_name) +1];strcpy(m_name,name);}
    ~X()                 {delete [] m_name;}
};

This looks fine. 看起来很好。 But the compiler is also generating two methods for you. 但编译器也为您生成两种方法。 In mnost situations this is fine but not when you have a RAW owned pointer. 在mnost情况下这很好,但是当你有一个RAW拥有的指针时。

X::X(X const& copy)
    :m_name(copy.m_name)
{}

X& X::operator=(X const& copy)
{
    m_name = copy.m_name;
}

Now image the code: 现在成像代码:

X    x("Martin");
X    y(x);

Both 'x' and 'y' now contan pointers (m_name) pointing at the same piece of memory. “x”和“y”现在都指向同一段内存的指针(m_name)。 When 'y' goes out of scope it calls its derstructor which calls the delete [] on the memory. 当'y'超出范围时,它会调用它的derstructor,它会调用内存中的delete []。 Now 'x' goes out of scope and calls delete on the same piece of memory. 现在'x'超出范围并在同一块内存上调用delete。

Z    z("Bob");
z = x;

Same problem as above jsut using a different operator. 与使用不同运算符的上述jsut相同的问题。

How does this apply to you? 这对你有什么用?
You are using a map of pointer to Column. 您正在使用指向Column的指针映射。 The map actually stores a Coloumn object. 地图实际上存储了一个Coloumn对象。 So it is using the Copy constructor above to make a copy of your object. 所以它使用上面的Copy构造函数来制作对象的副本。 So there is a probelm. 所以有一个问题。 But also in code there is a lot of times when temporary objects are created and passed around. 但是在代码中,很多时候创建和传递临时对象。

doWork(Column const& x) { /* Do somthing intersting */

doWork(Column("Hi There"));

Here we create a temporary Column object that is passed to the doWork(). 这里我们创建一个传递给doWork()的临时Column对象。 When doWork() is complete the temporay goes out of scope and is deleted. 当doWork()完成时,临时超出范围并被删除。 But what happens if doWork() makes a copy of the object using either the copy costructor or assignment operator? 但是,如果doWork()使用复制结构函数或赋值运算符复制对象,会发生什么? This is what is happening when you insert the object into the map. 这是将对象插入地图时发生的情况。 You are creating a temporary pair then copying this value into the map. 您正在创建一个临时对,然后将此值复制到地图中。 This temporarty pair is then being destroyed. 然后这个临时对被摧毁。

The underlying reason why your string m_Name doesn't print the second time is because of the way the STL builds a map. 字符串m_Name第二次不打印的根本原因是STL构建映射的方式。 It makes various copies of the value during its insertion. 它在插入过程中会生成各种值的副本。 Because of this, m_Name gets destroyed in one of the copies of the original column. 因此,m_Name在原始列的一个副本中被销毁。

Another piece of advice is to use pointers to objects when the object is a value in the map. 另一条建议是当对象是地图中的值时使用指向对象的指针。 Otherwise you could be taking a major performance hit of the object is large enough. 否则你可能会对该对象的主要性能影响足够大。

The m_Name in your container should be a string, so it can be copy-constructed into a map. 容器中的m_Name应该是一个字符串,因此可以将其复制到地图中。

Right now you are not defining a proper copy constructor, so it's only copying m_Name as a pointer, which is invalid. 现在你没有定义一个合适的拷贝构造函数,所以它只是将m_Name复制为指针,这是无效的。

try simplifying by doing 尝试通过做简化

class Column {
private:
    std::string m_Name;
public:
    // Overrides
    const char *Name(){
        return m_Name.c_str();
    }
};

You get the copy constructor for free thanks to C++ and all your members being copy constructible. 由于C ++,您可以免费获得复制构造函数,并且所有成员都可以复制构造。

Why not using a std::string for the Column name ? 为什么不使用std::string作为列名? Really ? 真的吗? And then everything's alright. 然后一切都好。

Because here you've got a number of problems... beginning with your destructor (use delete[] when allocation is done by new[] ) 因为在这里你有很多问题......从你的析构函数开始(当new[]完成分配时使用delete[] new[]

Also, do you really need to create your column with new ? 另外,您真的需要使用新的列创建列吗?

Let's rewrite that: 让我们改写:

class Column
{
public:
  Column() : m_name() {}
  Column(const std::string& name) : m_name(name) {}

  const std::string& getName() const { return m_name; }

private:
  std::string m_name;
};

And now your insertion code: 现在你的插入代码:

std::map<int,Column> m_container;

Column myColumn = Column("Test");
std:cout << "CREATED: " << myColumn.getName() << std::endl;
m_container[0] = myColumn; // COPY the column
std::cout << "AGAIN: " << myColumn.getName() << std::endl;

And here everything is fine. 这里一切都很好。 And even slickier syntax 甚至更流畅的语法

m_container[0] = Column("Test");

C++ already requires a fair amount of code bloat, let's use the shortcuts it offers whenever possible. C ++已经需要相当多的代码膨胀,让我们尽可能使用它提供的快捷方式。

What you're seeing is that a temporary copy of Column is being destroyed. 您所看到的是正在销毁Column的临时副本。 If you instrument the constructor you should see the copy being created. 如果您检测构造函数,则应该看到正在创建的副本。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM