Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Pointer: changing the contents without changing the address?

Tags:

c++

pointers

MyCustomObject * object=new MyCustomObject();

Assume the object pointer is used by many of my classes, but all of a sudden I want to change the contents of the pointer without changing the address.

Am I wrong in thinking that object = new MyCustomObject() will give a new pointer address to the object? I want the new object without changing the pointer address (Yes, I will make sure to clean the old object).

like image 630
Jeremie D Avatar asked Jun 05 '13 16:06

Jeremie D


People also ask

Can pointer value be changed?

Modifying the value of a pointerWe can change the pointer's value in a code, but the downside is that the changes made also cause a change in the original variable.

Does pointers have its own address?

The main feature of a pointer is its two-part nature. The pointer itself holds an address. The pointer also points to a value of a specific type - the value at the address the point holds.

Can two different pointers point to same address?

Yes, two pointer variables can point to the same object: Pointers are variables whose value is the address of a C object, or the null pointer.

What is difference between pointer and address?

In C or C++ Programming Language, it is known that pointers hold the address of the variables or any memory location. If pointers are pointed to the memory location, it can be used to change the value of the variable. As for functions, any pointer can be passed by itself, or by the address of the pointer.


3 Answers

Generally it is preferable to change an object’s properties (by calling its methods) rather than deleting it and creating a new one. In particular, you can completely replace the object by assignment, such as:

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

Instead of the default constructor, you could use an existing instance of the object (e.g., some static object you define for this purpose) or the result of a function that returns the desired object.

However, you can delete an object but retain its space by calling its destructor, and you can create a new object in the same place by using placement 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;
}

After calling the destructor and the placement new, pointers to the old object point to the new object, since it is in the same place the old object was.

like image 85
Eric Postpischil Avatar answered Oct 15 '22 04:10

Eric Postpischil


Objects in Memory

This is a matter of understand how memory works for objects in C++

Lets imagine we have the following object:

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

It's size will be 20. (In most platforms). So in memory it will look like this:

Bytes
name             age
0000000000000000|0000|

You can change memory manually so you could create the object by doing something like:

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

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

You can prove object is successfully created by doing something like:

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

Which outputs:

In static manual memory the name is Fel 21 years old

By obvious practical reasons this is not used in real life, but it helps understand how objects are storage.

New operator

New operator basically works as this:

  1. Allocate the size of the object in bytes in the heap memory
  2. Call the object constructor to fill the memory

It's more complicated depending on the implementation, but the idea is the same.

So if the constructor is:

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

This code:

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

Will be almost equivalent to:

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

As I said is a little bit more complicated than that, but the idea is the same.

Replacing an object in memory

Now that this is understood there are many ways to replace an object in the same memory space, the most common way is placement new. Many examples below:

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

With output:

In static manual memory the name is Fel 21 years old

In static object the name is John

In static memory the name is Jenny

In dynamic memory the name is Xavier

In a dynamic object the name is Charles

Pointer of dynamic object is 4F8CF8

New name of dynamic object is Charly

Pointer of dynamic object is 4F8CF8

New name of dynamic object is Charl

Pointer of dynamic object is 4F8CF8

New name of dynamic object is Sandy

Pointer of dynamic object is FD850

like image 20
felknight Avatar answered Oct 15 '22 04:10

felknight


I think you are having difficulty with the concepts of pointer vs object.

An object is an instance of some type. Be it a base type like an int or a user defined type like a struct or class.

Using new operator, you create a new instance of this type inside of the process's memory called the heap, which is like a room with a pile of clothes in it. So if you create a new instance of a t-shirt, it is like you went out and bought a t-shirt and threw it into that pile. You know that you have an instance of a t-shirt in there somewhere, but you don't really know where.

A pointer points to an object (usually). Think of it like a piece of string that is attached to your t-shirt on one end and you have the other. This allows you to pull out your t-shirt and do stuff with it. Multiple pieces of string can be attached to that t-shirt with different people holding on to each piece, allowing each person to pull it out and use it. Remember, there is only one t-shirt, just multiple pointers to it.

Copying a pointer is like having a new piece of string attaching itself to the object. You do this by assigning one pointer to another tshirt* x = new tshirt(); tshirt* y = x;

A pointer though is a bit dangerous, because it can actually not point to anything. Usually when a programmer wants to recognise that a pointer is not valid, they assign it to NULL, which is a value of 0. In C++11, nullptr should be used instead of NULL for type safety reasons.

Further, if you use the delete operator on a pointer, you are deleting the object it is pointing at. The pointer you just deleted through and any copy of that pointer is then said to be dangling, which means that it is pointing at a memory location that doesn't actually contain a valid object. As a programmer, you must set the value of these pointers to NULL/nullptr or you will have difficult to track down bugs in your code.

The dangling pointer problem can be mitigated using smart pointers like std::unique_ptr and others (if you go through that link, hover over "Dynamic memory management" to get info on more pointer wrappers). These wrappers try and stop you inadvertently creating dangling pointers and memory leaks.

A memory leak is when you create an object using the new operator and then lose the pointer to it. Going back to the pile of clothes analogy, it would be like you dropped your piece of string and thus forgot that you have t-shirt in there, so you go out and buy another one. If you keep doing this, you will find that your pile of clothes may fill the room and eventually cause the room to explode as you have no more room for more t-shirts.

So to get back to your question, to change the contents of an object that you created using the new operator, you can dereference that pointer using the indirection operator (*) or you can call a member function or get/set a member value of the object using the structure dereference operator (->). You are correct that the object = new MyCustomObject() will give you a new pointer address because it has created a new object. If you just want to new pointer that points at the same object, you would do something like this:

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

Note that I prefixed the variable name with p, I (and others) use this to allow me at a glance to determine if the variable is an object or a pointer to an object.

Objects can be created directly on the function call stack without the new keyword.

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

These objects are automatically destroyed when the function call ends (or in some cases sooner).

Advanced

I'm thinking that this is not really what you wanted, but I have just added it for completeness.

There have been references to placement new, which reuses memory that has already been allocated. This is a valid but quite an advanced feature. You have to be careful to call the destructor through the pointer (i.e. pObject1->~MyCustomObject()) prior to doing the in place new (i.e. pObject1 = new (pObject1) MyCustomObject()) otherwise you can have a resource leak (files or may be left open or other resources may be not cleaned up).

Good luck.

like image 38
Adrian Avatar answered Oct 15 '22 03:10

Adrian