如何在不复制的情况下取消引用使用 New 创建的指针

How can I dereference a pointer created with New without copying?

本文关键字:New 指针 创建 引用 情况下 复制 取消      更新时间:2023-10-16

在下面的代码片段中,我通过 new 运算符创建一个对象并将其分配给指针。我想做的是将它指向的对象添加到向量中。似乎正在发生的事情是我正在很好地创建对象,然后当我取消引用它时,这将创建对原始对象的引用。因此,当我将此引用分配给向量时,它会创建一个副本,而我想要实际的对象。任何帮助将不胜感激。

std::vector<Object> OBJVec;
Object *O = new Object();
/*Edits some of the internals*/
addToVec(*O); //Here is the beginning of the problem: this passes a reference to the content of O.
void addToVec(Object OParam){ //Here Creates a copy of O instead of passing the actual content.
    OBJVec.push_back(OParam); // Pushes a copy of content instead of content.
}

*注意:上面的代码是我试图完成的代码的非常简化的版本。

关于此接口的代码的说明,此代码与 OpenGL 交互,因此 OBJVec 必须包含对象。

我已经在这个问题上工作了很长时间,任何帮助都会很棒。

行为是正确的

这是std::vector::push_back的签名

void push_back( const T& value );
void push_back( T&& value );

取消引用指针时,现在具有对原始对象的引用。将左值引用传递给push_back时,将选取第一个重载。

在您的第二种情况下,它也是一个左值,因此仍然选择第一个重载......您无法在push_back上躲避复制,除非您传递rvalue或明确std::move

如果你想要指针,那就让你的容器成为指针的容器。如果需要引用,请让您的容器对您的对象reference_wrapper

但是,如果您执行这些路由,则需要执行额外的内存管理工作

由于std::vector处理自己的内存分配(默认情况下)1,因此您不能在其外部分配元素,然后使向量指向它,除非您使向量value_type本身成为指针,即std::vector<Object*>,并将指针本身传递给addToVec

std::vector<Object*> OBJVec; // Make a vector of pointers instead of objects
Object *O = new Object();
addToVec(O); // Do not dereference here
void addToVec(Object *OParam) { // Use a pointer here as the argument
    OBJVec.push_back(OParam);
}

但是,一旦将元素从向量中删除并且不再使用它们,就需要解除分配元素。为了简化这一点,您可以改用 unique_ptr s 的向量,这是智能指针,一旦不再需要,就会为您释放对象。

如果需要指针向量,则无法应用此解决方法。然后,您不能对向量之外的每个对象都使用 new。这必然会导致对象被单独分配,而不能确保连续的内存布局,这是任何采用数组的 OpenGL 函数所要求的。您需要分配一个带有new Object[count]的数组,或者干脆完全放弃手动内存的想法,只使用对象向量而不分配其外部的元素。


1)你可以改变这种行为,但仍然不可能像你目前所做的那样,在向量之外单独分配每个元素。

我认为这应该更好(这样你就不会复制任何东西):

std::vector<Object*> OBJVec;
Object *O = new Object();
addToVec(O); 
void addToVec(Object* OParam){
    OBJVec.push_back(OParam);
}

在 C++98 中没有移动的概念,因此您必须改用指针向量并在那里复制对原始对象的引用。在 C++11 中,您确实具有移动语义,因此您可以使用 std::move 从字面上移动对象。但是,我不确定这在您的情况下是否是一个好主意。

首先,如今使用裸new不被认为是一种好习惯。您应该使用一些智能指针或您自己的 RAII 包装器。在您的情况下,我只需使用std::unique_pointer(C++11 - 或boost::unique_pointer),然后只是一个可以安全地移动指针的std::vector<std::unique_pointer<your_type>>(您无法复制std::unique_pointer)。这种方法没有真正的开销,因此如果您需要堆分配和唯一所有权,这可能是要走的路。

如果你不需要堆分配并且你使用的是 c++11,那么你一开始就不需要使用指针。只需创建您的对象并随时将其移动到矢量内 - 但是,请确保您永远不会再次使用原始对象!