C++ C++;具有动态分配的字符数组的结构

C++ C++;具有动态分配的字符数组的结构,c++,memory-management,struct,dynamic,destructor,C++,Memory Management,Struct,Dynamic,Destructor,我试图将结构存储在向量中。Struct需要为给定大小的char*动态分配内存。 但一旦我将结构添加到向量,它的析构函数就会被调用,就像我丢失了指向它的指针一样 为了举例,我制作了这个小演示 #include "stdafx.h" #include <iostream> #include <vector> struct Classroom { char* chairs; Classroom() {} // default constructor

我试图将结构存储在向量中。Struct需要为给定大小的char*动态分配内存。 但一旦我将结构添加到向量,它的析构函数就会被调用,就像我丢失了指向它的指针一样

为了举例,我制作了这个小演示

#include "stdafx.h"
#include <iostream>
#include <vector>

struct Classroom
{
    char* chairs;

    Classroom() {} // default constructor

    Classroom(size_t size)
    {
        std::cout << "Creating " << size << " chairs in a classroom" << std::endl;
        chairs = new char[size];
    }

    ~Classroom()
    {
        std::cout << "Destroyng chairs in a classroom" << std::endl;
        delete[] chairs;
    }
};

std::vector<Classroom> m_classrooms;

int main()
{

    m_classrooms.push_back(Classroom(29));
    //m_classrooms.push_back(Classroom(30));
    //m_classrooms.push_back(Classroom(30));

    system("Pause");

    return 0;
}
是的,似乎析构函数被调用了两次!一次添加到向量,第二次在程序完成其执行时

当我尝试使用类而不是结构时,也会发生完全相同的情况

有人能解释为什么会发生这种情况,以及正确完成任务的可能方法吗?

@LPVOID

使用emplace_back(..)在适当位置创建对象可以帮助您避免此处面临的
双重释放或损坏
错误

m_教室。向后安置(29)

但是,更好的做法是始终遵循3/5/0规则,以避免指针悬空结束

@LPVOID

使用emplace_back(..)在适当位置创建对象可以帮助您避免此处面临的
双重释放或损坏
错误

m_教室。向后安置(29)


但是,更好的做法是始终遵循3/5/0规则,以避免指针悬空结束

教室
类不能在
std::vector
中安全使用,因为它的复制语义不正确。
std::vector
将复制对象,如果复制语义有bug,那么当您开始在容器(如
vector
)中使用该类时,您将看到所有这些bug都表现出来

为了使您的类具有正确的复制语义,它需要能够无错误地构造、分配和销毁自身的副本(这些错误包括内存泄漏、对同一指针的双重删除调用等)

代码中缺少的另一件事是类中需要知道
size
参数。现在,您发布的只是内存分配,但是没有任何东西可以保存
大小。如果不知道分配了多少个字符,就不可能正确实现用户定义的复制构造函数和赋值运算符,除非
char*
是以空结尾的字符串


话虽如此,有多种方法可以修复您的类。最简单的方法是简单地使用内置了正确复制语义的类型,而不是自己处理原始内存。这些类包括
std::vector
std::string
。它们不仅自己清理,而且这些类知道自己的大小,而不必携带
size
成员变量

struct Classroom
{
    std::vector<char> chairs;

    Classroom() {} // default constructor
    Classroom(size_t size) : chairs(size)
    {
        std::cout << "Creating " << size << " chairs in a classroom" << std::endl;
    }
};
注意初始化类的成员时成员初始化列表的用法。还要注意在实现赋值运算符时的用法


纠正的另一个问题是默认构造函数没有初始化所有成员。因此,在原始类中,可以使用简单的单行程序,例如:

int main()
{
   Classroom cr;
}
会导致问题,因为在析构函数中,您会删除未初始化的
指针


在此之后,
std::vector
现在应该能够安全地使用。
教室
类不能在
std::vector
中安全地使用,因为它具有不正确的复制语义。
std::vector
将复制对象,如果复制语义有bug,那么当您开始在容器(如
vector
)中使用该类时,您将看到所有这些bug都表现出来

为了使您的类具有正确的复制语义,它需要能够无错误地构造、分配和销毁自身的副本(这些错误包括内存泄漏、对同一指针的双重删除调用等)

代码中缺少的另一件事是类中需要知道
size
参数。现在,您发布的只是内存分配,但是没有任何东西可以保存
大小。如果不知道分配了多少个字符,就不可能正确实现用户定义的复制构造函数和赋值运算符,除非
char*
是以空结尾的字符串


话虽如此,有多种方法可以修复您的类。最简单的方法是简单地使用内置了正确复制语义的类型,而不是自己处理原始内存。这些类包括
std::vector
std::string
。它们不仅自己清理,而且这些类知道自己的大小,而不必携带
size
成员变量

struct Classroom
{
    std::vector<char> chairs;

    Classroom() {} // default constructor
    Classroom(size_t size) : chairs(size)
    {
        std::cout << "Creating " << size << " chairs in a classroom" << std::endl;
    }
};
注意初始化类的成员时成员初始化列表的用法。还要注意在实现赋值运算符时的用法


纠正的另一个问题是默认构造函数没有初始化所有成员。因此,在原始类中,可以使用简单的单行程序,例如:

int main()
{
   Classroom cr;
}
会导致问题,因为在析构函数中,您会删除未初始化的
指针


在此之后,现在应该可以安全地使用
std::vector

是否需要使用
char*
std::string
是字符串数据的正常选择,只起作用™ 在管理内存方面,您需要提供一个复制构造函数,它实际复制
成员指向的数据;否则,
push_back
调用(调用默认副本)只会复制指针。然后,当任何这样的副本被销毁时,指针将失效。请参阅。@chris我使用的是
char*
,因为我需要存储原始字节
std::vector