c++指针:在不改变地址的情况下改变内容
C++ Pointer: changing the contents without changing the address?
MyCustomObject * object=new MyCustomObject();
假设对象指针被我的许多类使用,但突然我想在不改变地址的情况下改变指针的内容。
我认为object = new MyCustomObject()
会给对象一个新的指针地址是错误的吗?我希望新对象不改变指针地址(是的,我将确保清理旧对象)。
一般来说,最好是改变一个对象的属性(通过调用它的方法),而不是删除它并创建一个新的。特别是,您可以通过赋值完全替换对象,例如:
*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
之后,指向旧对象的指针指向新对象,因为它位于旧对象所在的位置。
内存中的对象
这是一个理解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 oldn",
reinterpret_cast<SimpleObject*>(staticMemory)->name
,reinterpret_cast<SimpleObject*>(staticMemory)->age);
输出:在静态手动内存中,名字是Fel 21岁
由于明显的实际原因,这在现实生活中没有使用,但它有助于理解对象是如何存储的。
<标题>新操作符New操作符基本如下:
- 在堆内存中以字节为单位分配对象的大小
- 调用对象构造函数来填充内存
根据实现的不同会更复杂,但思想是一样的。
如果构造函数是:
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;
就像我说的,比那要复杂一点,但思想是一样的。
替换内存中的对象
现在理解了这一点,有许多方法可以在相同的内存空间中替换对象,最常见的方法是放置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 oldn",
reinterpret_cast<SimpleObject*>(staticMemory)->name
,reinterpret_cast<SimpleObject*>(staticMemory)->age);
//Static object
new (&staticObject) SimpleObject("John",23);
printf("In static object the name is %sn",staticObject.name);
//Static memory
SimpleObject* staticMemoryObject = reinterpret_cast<SimpleObject*>(staticMemory);
new (staticMemoryObject) SimpleObject("Jenny",21);
printf("In static memory the name is %sn",staticMemoryObject->name);
//Dynamic memory (heap)
void* dynamicMemoryObject = malloc( sizeof(SimpleObject) );
new (dynamicMemoryObject) SimpleObject("Xavier",22);
printf("In dynamic memory the name is %sn",reinterpret_cast<SimpleObject*>(dynamicMemoryObject)->name);
free(dynamicMemoryObject);
//Dynamic object
SimpleObject* dynamicObject = new SimpleObject("Charles",31);
printf("In a dynamic object the name is %sn",dynamicObject->name);
printf("Pointer of dynamic object is %8Xn",dynamicObject);
//Replacing a dynamic object with placement new
new (dynamicObject) SimpleObject("Charly",31);
printf("New name of dynamic object is %sn",dynamicObject->name);
printf("Pointer of dynamic object is %8Xn",dynamicObject);
//Replacing a dynamic object with stack object
SimpleObject stackObject("Charl",31);
memcpy(dynamicObject,&stackObject,sizeof(SimpleObject));
printf("New name of dynamic object is %sn",dynamicObject->name);
printf("Pointer of dynamic object is %8Xn",dynamicObject);
//Replacing a dynamic object with a new allocation
dynamicObject = new SimpleObject("Sandy",22);
printf("New name of dynamic object is %sn",dynamicObject->name);
printf("Pointer of dynamic object is %8Xn",dynamicObject);
return 0;
}
与输出:标题>在静态手动内存中,名字是Fel 21岁
静态对象的名称是John
静态内存中的名字是Jenny
在动态内存中名为Xavier
在动态对象中,名称是Charles
动态对象指针为4F8CF8
动态对象的新名称为Charly
动态对象指针为4F8CF8
动态对象的新名称为Charl
动态对象指针为4F8CF8
动态对象的新名称为Sandy
动态对象指针为FD850
我想你对指针和对象的概念有困难。
对象是某种类型的实例。可以是像int这样的基类型,也可以是像struct或类这样的用户自定义类型。
使用new
操作符,在进程的内存中创建一个这种类型的新实例,称为堆,它就像一个房间,里面有一堆衣服。所以如果你创建一个t恤的新实例,就像你出去买了一件t恤,然后把它扔到那堆东西里。你知道在某个地方有一个t恤的实例,但是你不知道在哪里。
指针指向一个对象(通常是)。把它想象成一根绳子,一端系在你的t恤上,另一端系在你身上。这可以让你拿出你的t恤,用它做点什么。t恤上可以系上多根绳子,由不同的人抓住每一根,每个人都可以把它拉出来使用。记住,这里只有一件t恤,只是指向它的多个指针。
复制指针就像在对象上附加了一个新的字符串。你可以将一个指针赋值给另一个tshirt* x = new tshirt(); tshirt* y = x;
指针有点危险,因为它实际上不能指向任何东西。通常,当程序员想要识别指针无效时,他们会将其赋值给NULL
,这是一个值0。在c++ 11中,出于类型安全的考虑,应该使用nullptr
而不是NULL
。
此外,如果在指针上使用delete
操作符,则删除了它所指向的对象。刚才删除的指针和该指针的任何副本都被称为悬空的,这意味着它指向一个实际上不包含有效对象的内存位置。作为一名程序员,你必须将这些指针的值设置为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.
这些对象在函数调用结束时(或在某些情况下更早)自动销毁。
先进我认为这不是你真正想要的,但我只是为了完整而添加了它。
有对位置new的引用,它重用了已经分配的内存。这是一个有效但相当高级的功能。在执行到位的new(即pObject1 = new (pObject1) MyCustomObject()
)之前,必须小心地通过指针(即pObject1->~MyCustomObject()
)调用析构函数,否则可能会发生资源泄漏(文件或可能保持打开状态或其他资源可能未被清理)。
好运。
指针是对象,所以如果您执行new Object()
,则创建一个新的指针,但具有前一个指针的函数将看不到它。如果你想改变内容,你可以这样做,因为所有知道指针的其他对象都会引用你的对象包含的值。它有点类似于全局变量。所以你可以传递指针,所有的函数在访问对象的时候都会看到你的改变。
每次通过new
操作符创建一个新对象时,系统都会在堆上为它寻找一个好的地址。你不能命令它给你一个特定的地址,即使你知道它是免费的和可用的。
你唯一能做的就是改变属性对象的特定地址,如存储在int
变量中的数字。
你是对的,运算符new
返回一个新地址,或者说是系统决定的地址。
实现返回相同地址的分配器,并在new和delete操作符重载中使用。
- 在没有太多条件句的情况下,我如何避免被零除
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 在未初始化映射的情况下,将值插入到映射的映射中
- 是默认情况下分配给char数组常量的值
- 为什么我不能在不创建字符串变量的情况下使用函数的字符串输出
- 如何在不产生任何垃圾的情况下获得C中的像素
- C++去除前x个元素的有效方法,在不改变向量大小的情况下将第x+1个元素推到第一个
- 如何在不改变原始数组的情况下将数组作为参数传递
- 如何在不改变顺序的情况下将int64值移位到无符号int64空间
- Tetgen:从曲面的给定三角形开始,在不改变曲面连接性的情况下创建新网格
- 在c++中不改变倒数第二位的情况下移动整数中的位
- 为什么在const仍然是const的情况下改变了指向const的指针的差异?
- c++指针:在不改变地址的情况下改变内容
- 在不改变精灵位置的情况下缩放资产
- 在不改变索引顺序的情况下从动态数组中删除元素
- 在不改变接口的情况下为对象增加功能
- 为什么这个函数在没有指针的情况下改变列表值
- stereoCalibrate()即使在不应该改变焦距的情况下也会改变焦距
- 在c++中是否有一个很好的理由在不改变属性的情况下引用它?
- 在不丢失数据的情况下改变帧类型