0

I would like to be able to call a non-trivial constructor on an object when I use the push_back() method. Instead, all I have been able to do is pass a shallow copy of an object to the vector. When using regular c-style arrays, the constructor is automatically called when the array is created, but since vectors are dynamic, the object don't exist yet.

How would I go about adding a new, empty object to a vector without having two pointers point to the same memory address? Do I need a copy constructor or an overloaded assignment operator?

Here is some example code that demonstrates my problem:

struct Object
{
    int *Pointer;

    Object()
    {
        Pointer = new int;
        *Pointer = 42;
    }

    ~Object()
    {
        delete Pointer;
    }
};


int main()
{
    std::vector <Object> *Array;
    Array = new std::vector <Object>;

    // Attempt 1
    Array->push_back(Object());

    // Attempt 2
    {
        Object LocalInstance;
        Array->push_back(LocalInstance);
    }

    // Error here because the destructor has already been called for 
    // LocalInstance and for Object()
    delete Array;

    return 0;
}
  • 3
    Don’t use pointers here. It’s unnecessary, and makes the code less efficient and more complicated. – Konrad Rudolph Mar 14 '15 at 18:02
  • This is a simplification of the actual code, in order to make the problem easier to see / debug. The actual code must have these pointers. – JubileeTheBear Mar 14 '15 at 18:05
  • Even if you really need a pointer, you should prefer smart pointers in STL containers such as [boost::shared_ptr](http://www.boost.org/doc/libs/1_57_0/libs/smart_ptr/shared_ptr.htm) or [std::shared_ptr](http://en.cppreference.com/w/cpp/memory/shared_ptr). – dbank Mar 14 '15 at 18:15
  • @dbank I think you mean [`std::unique_ptr`](https://en.cppreference.com/w/cpp/memory/unique_ptr) – Caleth Nov 23 '21 at 15:38
  • @Caleth Hmm... I'm not sure what was going through my mind back when I made that comment, but yes, I agree that unique_ptr's would be my default choice for smart pointers unless there was an actual need for shared ownership. – dbank Nov 24 '21 at 17:04

2 Answers2

1

@KonradRudolph is correct - there is no reason for Array to be a pointer here and it just obfuscates the code.

The reason your code crashes is because LocationInstance is passed to push_back but your Object doesn't have a proper copy constructor, so only a shallow copy of the Pointer member is copied. When the LocalInstance goes out of scope, that object deletes its Pointer but the copy in the container has the same Pointer, which results in deleting freed memory when the container is cleaned up.

Please check out the Rule-of-Five (was Rule-of-Three before C++11).

In terms of construction objects as you add them to the container, are you using C++11? If so, you can emplace_back and construct the object in the container as it is added - is that what you're looking to do?

This trivial example only gives me a warning with clang:

struct A
{
    A(int a) : member_m(a)
    {}

    int &member_m;
};
int main() {
    A a(1);
}

gives me:

~/ClionProjects/so_reference_to_temporary $ clang++ -o test main.cpp
main.cpp:8:25: warning: binding reference member 'member_m' to stack allocated
      parameter 'a' [-Wdangling-field]
    A(int a) : member_m(a)
                        ^
main.cpp:13:10: note: reference member declared here
    int &member_m;
         ^
1 warning generated.
Community
  • 1
  • 1
sfjac
  • 7,119
  • 5
  • 45
  • 69
  • I used a pointer for the array of objects because without it an error does not get thrown (at least not for me). What would happen is the memory would be deallocated, but the pointer member of the object in the aray would point to an invalid memory address. I dynamically allocated the array in order to force the compiler to generate an error when the destructor was called twice. – JubileeTheBear Mar 14 '15 at 18:09
  • 1
    That would just be "luck" - deleting freed memory is undefined behavior. As with many memory bugs, sometimes, when you're lucky, it will crash. Sometimes it will just silently work correctly until it doesn't. Tools like valgrind are very helpful for checking ones code for such issues. – sfjac Mar 14 '15 at 18:10
  • You were right, all I needed was a copy constructor. For some reason it didn't work the first time, but now it does. – JubileeTheBear Mar 14 '15 at 19:08
0
#include <iostream>

using namespace std;

// constructors examples 

class myClass 
{
    private:
        int* data;
    public:
        //constructor 
        myClass(int value)
        {
            data = new int ;
            *data = value ;

        } 

        //copy constructor 
        myClass (const myClass& other)
            :myClass(*other.data)
        {
                
        }

        //move constructor 
        myClass (myClass&& other)
            :data(other.data)
        {
            other.data = nullptr ;
             
        }

        //destructor 
        ~myClass ()
        {
            delete data ;
            
        }
        //copy assignment 

        myClass& operator=(myClass other)
        {
            std::swap (data, other.data);   
            return *this ;
        }

};

int main ()
{
    myClass M1(5);
    myClass M2=M1;
    myClass M3(M2);
    M1 = M3;
    myClass M4(std::move(M1));
    return 0;
}
  • 2
    How does that piece of code answer OP's question? Please edit and explain. – Eric Aya Nov 23 '21 at 15:43
  • 1
    Please don't post only code as an answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Tyler2P Nov 23 '21 at 20:24