繁体   English   中英

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

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

我正忙着使用map和valuetype分配。

考虑这个简单的类:

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;
    }
};

现在我有这张地图:

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

当我这样称呼:

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";

控制台正在打印“wtf?” 插入地图后。

它似乎正在摧毁这个专栏。 这是正确的吗?

或者我做错了什么?

我想知道std::mapvalue_type是否具有定义内存大小的结构类型,如POD还是POD数组?

cout << AGAIN不打印“测试”

我需要的是基于int键的列映射

make_pair(0, *c)创建一个(临时的,未命名的) pair<int, Column> 这对通过引用传递给map::insert ,并且由于std::map拥有它的元素,因此它会复制该对。 然后,销毁临时对,销毁它包含的Column对象。

这就是为什么有必要为要用作标准容器元素的类型正确定义复制构造的原因。

您的代码中有什么好处:

在这里,您将动态创建一个对象。 (这不会在您的代码中释放)。
你为什么用指针? (智能指针是你的朋友。)
但是一个普通的物体会更好。

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

现在,您将对象的副本复制到std :: pair <int,Column>类型的临时对象中(这使用了Column的复制构造函数,该构造函数生成了M_name成员的副本(现在您有两个指向该对象的指针)记忆位置))。

std::make_pair(0, *c)

现在将对<int,Column>插入到地图中。 这是通过再次使用Column copy构造函数完成的(它使用调用Column复制构造函数的make_pair复制构造函数)。 M_name指针再次复制到此对象中。 所以现在你有三个对象在动态分配的字符串。

m_Container->insert( pairObject )

此时,不再需要通过调用std :: make_pair()创建的临时对象,因此它将被销毁。 这是析构函数被调用的地方。 当然,这会留下两个带有指针的对象,这些对象现在已经被释放了。

你有一个大问题。

您需要使用std :: string(首选解决方案)
或者您需要正确处理类中RAW拥有的指针。 这意味着您需要实现所有四个默认生成的方法:

  • 构造函数
  • destructror
  • 复制构造函数
  • 赋值运算符

小问题:

你需要像java程序员一样停止编码。

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

应该是这样的:

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

无需动态分配所有内容。
事实上非常危险。

解释为什么拥有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;}
};

看起来很好。 但编译器也为您生成两种方法。 在mnost情况下这很好,但是当你有一个RAW拥有的指针时。

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

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

现在成像代码:

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

“x”和“y”现在都指向同一段内存的指针(m_name)。 当'y'超出范围时,它会调用它的derstructor,它会调用内存中的delete []。 现在'x'超出范围并在同一块内存上调用delete。

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

与使用不同运算符的上述jsut相同的问题。

这对你有什么用?
您正在使用指向Column的指针映射。 地图实际上存储了一个Coloumn对象。 所以它使用上面的Copy构造函数来制作对象的副本。 所以有一个问题。 但是在代码中,很多时候创建和传递临时对象。

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

doWork(Column("Hi There"));

这里我们创建一个传递给doWork()的临时Column对象。 当doWork()完成时,临时超出范围并被删除。 但是,如果doWork()使用复制结构函数或赋值运算符复制对象,会发生什么? 这是将对象插入地图时发生的情况。 您正在创建一个临时对,然后将此值复制到地图中。 然后这个临时对被摧毁。

字符串m_Name第二次不打印的根本原因是STL构建映射的方式。 它在插入过程中会生成各种值的副本。 因此,m_Name在原始列的一个副本中被销毁。

另一条建议是当对象是地图中的值时使用指向对象的指针。 否则你可能会对该对象的主要性能影响足够大。

容器中的m_Name应该是一个字符串,因此可以将其复制到地图中。

现在你没有定义一个合适的拷贝构造函数,所以它只是将m_Name复制为指针,这是无效的。

尝试通过做简化

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

由于C ++,您可以免费获得复制构造函数,并且所有成员都可以复制构造。

为什么不使用std::string作为列名? 真的吗? 然后一切都好。

因为在这里你有很多问题......从你的析构函数开始(当new[]完成分配时使用delete[] new[]

另外,您真的需要使用新的列创建列吗?

让我们改写:

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;
};

现在你的插入代码:

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;

这里一切都很好。 甚至更流畅的语法

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

C ++已经需要相当多的代码膨胀,让我们尽可能使用它提供的快捷方式。

您所看到的是正在销毁Column的临时副本。 如果您检测构造函数,则应该看到正在创建的副本。

暂无
暂无

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

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