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

前端 未结 6 1785
悲&欢浪女
悲&欢浪女 2020-12-31 05:11
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 con

相关标签:
6条回答
  • 2020-12-31 05:23

    Everytime you create a new object via the new operator the system looks for a good address for it on the heap. You cannot instruct it to give you a certain address, even if you know it's free and usable.

    The only thing you can do is change properties of the object at a certain address, like the number stored in an int variable.

    You're right, the operator new returns a new address, or let's say the address the system decided.

    0 讨论(0)
  • 2020-12-31 05:30

    The pointer IS the object, so if you do new Object() you create a new one, but the functions with the prevoius pointer will not see it. If you want to change the content, you can do this, because all other objects which know the pointer will reference the values that your object contains. It's a bit similar to a global variable. So you can pass around the pointer and all functions will see your changes as soon as they acces the object.

    0 讨论(0)
  • 2020-12-31 05:38

    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

    0 讨论(0)
  • 2020-12-31 05:39

    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.

    0 讨论(0)
  • 2020-12-31 05:43

    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.

    0 讨论(0)
  • 2020-12-31 05:43

    Implement allocator to return same address, and use it in new and delete operator overloaded.

    0 讨论(0)
提交回复
热议问题