Home > OS >  push_back crashed when using std::vector if the type only defined copy constructor with c 11
push_back crashed when using std::vector if the type only defined copy constructor with c 11

Time:01-27

The code like this:

#include <iostream>
#include <vector>

struct Foo
{
    int i;
    double d;
};

class Boo
{
public:
    Boo() : fptr(nullptr) 
    {
        std::cout << "Boo default construct..." << std::endl;
    }
    Boo(int i, double d):fptr(new Foo{i,d})
    {
    }

    Boo(const Boo &rhs) :fptr(new Foo{rhs.fptr->i,rhs.fptr->d})
    {
    }

    Foo *fptr;
};

int main(int argc, char const *argv[])
{
    std::vector<Boo> vec(1);
    Boo b(42, 10.24);
    vec.push_back(b);
    return 0;
}

Env: Ubuntu 16.4 LTS with gcc 5.4 g -std=c 11 test.cpp -o test -g

./test

If define move constructor,this will work good. I debuged it several hours but can not found what's wrong with it, maybe because some rules in c 11 that i did't know.

Could somebody help me? Thanks!

CodePudding user response:

The problem is that in the copy constructor of Boo you're dereferencing a nullptr. This is because when you wrote:

std::vector<Boo> vec(1); //this creates a vector of size 1 using Boo's default constrcutor

This is what happens due to the above statement:

  1. The above statement creates a vector named vec of size 1 using Boo's default constructor.
  2. This means that there is already 1 Boo object inside vec. Moreover since this Boo object was created using the default constructor, its fptr is set to nullptr.

Now, when you wrote:

vec.push_back(b);// this adds/push_back object b onto the vector vec so that now its size will be 2

Now, these are the important things that you have to note here:

  1. The vector vec has already 1 Boo object inside it which has its fptr as nullptr.
  2. Now you're adding one more element b into the vector, due to which reallocation may happen. And if/when this reallocation happens, the copy constructor of Boo will be used.
  3. And when this copy constructor is used to copy the already present Boo object, it dereferences its fptr. But since that fptr is nullptr and we know that dereferencing a nullptr is undefined behavior, so this may cause the program to segmentation fault as in your case.

Solution

First, before dereferencing fptr you should check it is a null pointer or not.

Second, you must free the memory you allocated via new using delete. This can be done by adding a destructor for class Boo and using delete on fptr inside it.

If you don't free this memory then you will have memory leak in your program.

#include <iostream>
#include <vector>

struct Foo
{
    int i;
    double d;
};

class Boo
{
public:
    Boo() : fptr(nullptr) 
    {
        std::cout << "Boo default construct..." << std::endl;
    }
    Boo(int i, double d):fptr(new Foo{i,d})
    {
        std::cout <<"Boo parameterized const"<<std::endl;
    }

    Boo(const Boo &rhs) 
    {
        std::cout <<"Boo copy const"<<std::endl;
        //add a check before dereferencing
        if(rhs.fptr!= nullptr)
        {
           fptr = new Foo{rhs.fptr->i,rhs.fptr->d};
           std::cout<<"i: "<<rhs.fptr->i <<" d: "<<rhs.fptr->d<<std::endl;
        }
        else 
        {
            std::cout<<"nullptr found"<<std::endl;
        }
        
       
    }

    Foo *fptr;
    //add destructor
    ~Boo()
    {
        std::cout<<"Boo destructor"<<std::endl;
        //for debugging check if we got a null pointer
        if(fptr!=nullptr) //just a check before deleting, you can remove this. This is for printing(debugging) when we get a nullptr
        {
            delete fptr;
            fptr = nullptr;
            std::cout<<"delete done"<<std::endl;
        }
        else 
        {
            std::cout<<"null pointer found while delete"<<std::endl;
        }
        
    }
};

int main(int argc, char const *argv[])
{
    std::vector<Boo> vec(1);
    Boo b(42, 10.24);
    vec.push_back(b);
    return 0;
}

CodePudding user response:

Your Boo copy constructor derefernces a pointer fptr that may be null.

A std::vector::push_back may reallocate vector storage. Reallocating may use copy constructors.

That is what is happening in your program. You are copying a Boo that has a null pointer.

  •  Tags:  
  • Related