16
MyCustomObject * object=new MyCustomObject();

假设我的许多类都使用了对象指针,但是突然间我想在不更改地址的情况下更改指针的内容。

我认为object = new MyCustomObject()会给对象一个新的指针地址是错误的吗?我想要新对象而不更改指针地址(是的,我会确保清理旧对象)。

4

6 回答 6

14

通常,最好更改对象的属性(通过调用其方法)而不是删除它并创建一个新对象。尤其是可以完全通过赋值来替换对象,比如:

*object = MyCustomObject(); // Replace object with the result of default constructor.

您可以使用对象的现有实例(例如,您为此目的定义的一些静态对象)或返回所需对象的函数的结果,而不是默认构造函数。

但是,您可以通过调用其析构函数来删除对象但保留其空间,并且可以使用放置在同一位置创建新对象new

#include <iostream>

class MyObject
{
public:
    MyObject()  { std::cout << "I was created at " << (void *) this << ".\n"; }
    ~MyObject() { std::cout << "Farewell from "    << (void *) this << ".\n"; }
};


int main(void)
{
    // Allocate space and create a new object.
    MyObject *p = new MyObject;

    // Destroy the object but leave the space allocated.
    p->~MyObject();

    // Create a new object in the same space.
    p = new (p) MyObject;

    // Delete the object and release the space.
    delete p;

    return 0;
}

在调用析构函数和放置之后new,指向旧对象的指针指向新对象,因为它与旧对象在同一个地方。

于 2013-06-05T17:47:02.180 回答
10

内存中的对象

这是了解内存如何在 C++ 中为对象工作的问题

假设我们有以下对象:

class SimpleObject
{
public:
    char name[16];
    int age;
};

它的大小为 20。(在大多数平台上)。所以在内存中它看起来像这样:

Bytes
name             age
0000000000000000|0000|

您可以手动更改内存,以便您可以通过执行以下操作来创建对象:

//Manual assigment
staticMemory[0] = 'F';
staticMemory[1] = 'e';
staticMemory[2] = 'l';
staticMemory[3] = 0;

int* age = reinterpret_cast<int*>(&staticMemory[16]);
*age = 21;

您可以通过执行以下操作来证明对象已成功创建:

printf("In static manual memory the name is %s %d years old\n",
     reinterpret_cast<SimpleObject*>(staticMemory)->name
     ,reinterpret_cast<SimpleObject*>(staticMemory)->age);

哪个输出:

在静态手动记忆中,名字是 Fel 21 岁

由于明显的实际原因,这在现实生活中没有使用,但它有助于理解对象是如何存储的。

新运营商

新运算符基本上是这样工作的:

  1. 在堆内存中分配对象的大小(以字节为单位)
  2. 调用对象构造函数填充内存

根据实现的不同,它会更复杂,但想法是一样的。

所以如果构造函数是:

SimpleObject::SimpleObject(const char* name,int age)
{
    memcpy(this->name,name,16);
    this->age = age;
}

这段代码:

SimpleObject* dynamicObject = new SimpleObject("Charles",31);

将几乎等同于:

SimpleObject* dynamicMemoryObject = (SimpleObject*)malloc( sizeof(SimpleObject) );
memcpy(dynamicMemoryObject->name,"Charles",16);
dynamicMemoryObject->age = 31;    

正如我所说的,比这要复杂一些,但想法是一样的。

替换内存中的对象

既然理解了这一点,有很多方法可以替换同一内存空间中的对象,最常见的方法是placement new。下面有很多例子:

#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <new>

class SimpleObject
{
public:
    char name[16];
    int age;

    SimpleObject(const char* name,int age);
    SimpleObject()
    {
    }
};


SimpleObject::SimpleObject(const char* name,int age)
{
    memcpy(this->name,name,16);
    this->age = age;
}

//Object in static memory
SimpleObject staticObject;

//20 bytes in static memory
char staticMemory[20];

int main()
{
    //Manual assigment
    staticMemory[0] = 'F';
    staticMemory[1] = 'e';
    staticMemory[2] = 'l';
    staticMemory[3] = 0;

    int* age = reinterpret_cast<int*>(&staticMemory[16]);
    *age = 21;
    printf("In static manual memory the name is %s %d years old\n",
        reinterpret_cast<SimpleObject*>(staticMemory)->name
        ,reinterpret_cast<SimpleObject*>(staticMemory)->age);

    //Static object
    new (&staticObject) SimpleObject("John",23);
    printf("In static object the name is %s\n",staticObject.name);

    //Static memory
    SimpleObject* staticMemoryObject = reinterpret_cast<SimpleObject*>(staticMemory);
    new (staticMemoryObject) SimpleObject("Jenny",21);
    printf("In static memory the name is %s\n",staticMemoryObject->name);

    //Dynamic memory (heap)
    void* dynamicMemoryObject = malloc( sizeof(SimpleObject) );
    new (dynamicMemoryObject) SimpleObject("Xavier",22);
    printf("In dynamic memory the name is %s\n",reinterpret_cast<SimpleObject*>(dynamicMemoryObject)->name);
    free(dynamicMemoryObject);

    //Dynamic object
    SimpleObject* dynamicObject = new SimpleObject("Charles",31);
    printf("In a dynamic object the name is %s\n",dynamicObject->name);
    printf("Pointer of dynamic object is %8X\n",dynamicObject);

    //Replacing a dynamic object with placement new
    new (dynamicObject) SimpleObject("Charly",31);
    printf("New name of dynamic object is %s\n",dynamicObject->name);
    printf("Pointer of dynamic object is %8X\n",dynamicObject);

    //Replacing a dynamic object with stack object
    SimpleObject stackObject("Charl",31);
    memcpy(dynamicObject,&stackObject,sizeof(SimpleObject));
    printf("New name of dynamic object is %s\n",dynamicObject->name);
    printf("Pointer of dynamic object is %8X\n",dynamicObject);

    //Replacing a dynamic object with a new allocation
    dynamicObject = new SimpleObject("Sandy",22);
    printf("New name of dynamic object is %s\n",dynamicObject->name);
    printf("Pointer of dynamic object is %8X\n",dynamicObject);

    return 0;
}

带输出:

在静态手动记忆中,名字是 Fel 21 岁

在静态对象中,名称是 John

在静态内存中,名字是 Jenny

在动态内存中,名称是 Xavier

在动态对象中,名称是 Charles

动态对象的指针是 4F8CF8

动态对象的新名称是 Charly

动态对象的指针是 4F8CF8

动态对象的新名称是 Charl

动态对象的指针是 4F8CF8

动态对象的新名称是 Sandy

动态对象的指针是FD850

于 2013-06-05T18:43:27.607 回答
6

我认为您对指针与对象的概念有困难。

对象是某种类型的实例。无论是像 int 这样的基本类型,还是像结构或类这样的用户定义类型。

使用new运算符,您可以在进程的内存中创建一个这种类型的新实例,称为堆,它就像一个房间,里面有一堆衣服。因此,如果您创建 T 恤的新实例,就好像您出去买了一件 T 恤并把它扔进了那堆。你知道你在某处有一件 T 恤,但你真的不知道在哪里。

指针指向一个对象(通常)。把它想象成一根绳子,一端系在你的 T 恤上,另一端是你。这使您可以拉出 T 恤并用它做事。可以将多条绳子连接到该 T 恤上,不同的人抓住每条绳子,每个人都可以将其拉出并使用它。 请记住,只有一件 T 恤,只有多个指向它的指针。

复制指针就像将一条新字符串附加到对象上。您可以通过将一个指针分配给另一个指针来做到这一点tshirt* x = new tshirt(); tshirt* y = x;

指针虽然有点危险,因为它实际上不能指向任何东西。通常,当程序员想要识别指针无效时,他们会将其分配给NULL,即 0 值。在 C++11中,出于类型安全的原因,nullptr应该使用它而不是。NULL

此外,如果您delete在指针上使用运算符,您将删除它指向的对象。您刚刚删除的指针和该指针的任何副本都被称为dangling,这意味着它指向一个实际上不包含有效对象的内存位置。作为程序员,您必须将这些指针的值设置为NULL/ nullptr,否则您将难以追踪代码中的错误。

可以使用std::unique_ptr等智能指针来缓解悬空指针问题(如果您通过该链接,请将鼠标悬停在“动态内存管理”上以获取有关更多指针包装器的信息)。这些包装器试图阻止您无意中创建悬空指针和内存泄漏。

内存泄漏是当您使用运算符创建对象new然后丢失指向它的指针时。回到一堆衣服的比喻,就像你掉了一根绳子,从而忘记了你有T恤,所以你出去买另一件。如果你继续这样做,你会发现你的一堆衣服可能会填满房间并最终导致房间爆炸,因为你没有更多的空间容纳更多的 T 恤。

因此,回到您的问题,要更改您使用new运算符创建的对象的内容,您可以使用间接运算符 (*) 取消引用该指针,或者您可以调用成员函数或获取/设置使用结构取消引用运算符 (->) 的对象。你是对的,object = new MyCustomObject()因为它创建了一个新对象,所以会给你一个新的指针地址。如果您只想指向指向同一对象的新指针,您可以执行以下操作:

MyCustomObject* pObject1 = new MyCustomObject();

// ... do some stuff ...

pObject1->doStuff();
(*pObject1).doMoreStuff();

pObject1->value = 3;
(*pObject1).value = 4;

// ... do some stuff ...

// This copies the pointer, which points at original object instance
MyCustomObject* pObject2 = pObject1;

// Anything done to object pointed at by pObject2 will be seen via going
// through pointer pObject1.

pObject2->value = 2;
assert(pObject1->value == 2); // asserting that pObject1->value == pObject2->value

请注意,我在变量名称前加上了 p,我(和其他人)使用它来让我一目了然地确定变量是对象还是指向对象的指针。

对象可以直接在函数调用栈上创建,不用new关键字。

MyCustomObject object1;    // Note: no empty parenthesis ().
MyCustomObject object2(1); // Only use parenthesis if you actually are passing parameters.

当函数调用结束时(或者在某些情况下更早),这些对象会自动销毁。

先进的

我认为这不是您真正想要的,但我只是为了完整性而添加了它。

已经引用了placement new,它重用了已经分配的内存。这是一个有效但相当高级的功能。pObject1->~MyCustomObject()在执行就地新建(即)之前,您必须小心通过指针(即)调用析构函数,pObject1 = new (pObject1) MyCustomObject()否则您可能会发生资源泄漏(文件或可能保持打开状态或其他资源可能未清理)。

祝你好运。

于 2013-06-05T18:43:41.417 回答
-3

指针是对象,所以如果你new Object()创建一个新的,但带有 prevoius 指针的函数将看不到它。如果你想改变内容,你可以这样做,因为所有其他知道指针的对象都会引用你的对象包含的值。它有点类似于全局变量。因此,您可以传递指针,所有函数一旦访问对象就会看到您的更改。

于 2013-06-05T16:52:11.587 回答
-3

每次您通过new操作员创建一个新对象时,系统都会在堆上为其寻找一个好的地址。你不能指示它给你一个特定的地址,即使你知道它是免费和可用的。

您唯一能做的就是在某个地址更改对象的属性int,例如存储在变量中的数字。

你是对的,操作员new返回一个新地址,或者说系统决定的地址。

于 2013-06-05T16:54:14.843 回答
-3

实现分配器以返回相同的地址,并在重载的 new 和 delete 运算符中使用它。

于 2013-06-05T17:12:28.297 回答