1

I'm relatively new to C++, and I'm hoping someone can help me resolve an issue I'm having with unique_ptr and vectors. Essentially I'm trying to use polymorphism so that I have a vector of type "Base", which is an abstract class (pure virtual). I'm then attempting to fill this vector with derived classes. I've included a trivial example below, showcasing exactly what I'm trying to achieve. Please note that I need to use C++11, which is why I haven't made use of "std::make_unique". The code compiles fine, but I get run-time errors about "default_delete" in class Animal.

A related question is should I be using unique_ptrs for run-time polymorphism as below? Or should I be using raw pointers instead?

Header file and CPP files below. Error output from VS is included below this. Very many thanks in advance for any help with this.

HEADER FILE:

#ifndef START_H
#define START_H
#include <vector>
#include <memory>



class Animal
{
public:
    virtual ~Animal() = default;
    void run();
    void setNumLegs(int a) { numLegs = a; }
    const int getLegs() const { return numLegs; }
private:
    double numLegs;
    virtual void useLegs() = 0;
};

class Biped : public Animal
{
private:
    void useLegs();
};

class Multiped : public Animal
{
public:
    double costOfShoes{ 12.0 };
private:
    void useLegs();
    void payForShoes();
    void becomeDestitute();
};

class Farm
{
public:
    std::vector<std::unique_ptr<Animal>> animals;
};


class Countryside
{
public:
    std::vector<std::unique_ptr<Farm>> farms;

};
#endif // START_H

CPP FILE:

#include "start.h"
#include <iostream>

int main() {
    
    Countryside countryside;

    std::unique_ptr<Farm> f(new Farm);

    std::vector<int> legs = { 2,4,5,2,10 };
    for (auto& numLegs : legs) {
        if (numLegs == 2) {
            std::unique_ptr<Biped> biped(new Biped);
            biped->setNumLegs(numLegs);
            f->animals.push_back(std::move(biped));
        }
        else if (numLegs > 2) {
            std::unique_ptr<Multiped> multiped(new Multiped);
            multiped->setNumLegs(numLegs);
            f->animals.push_back(std::move(multiped));
        }
    }
    
    countryside.farms.push_back(std::move(f)); //THIS IS WHERE THE PROBLEM IS I THINK

    for (auto& animal : f->animals) {
        animal-> run();
    }
    return 0;
}

void Animal::run() 
{
    useLegs();
}

void Biped::useLegs()
{
    std::cout << "Running with: "<< getLegs() <<"legs\n";
}

void Multiped::useLegs()
{
    std::cout << "Running with many legs:" << getLegs() << "!!! legs\n";
    payForShoes();

}
void Multiped::payForShoes() 
{
    std::cout << "Paying for shoes...\n";
    becomeDestitute();
}

void Multiped::becomeDestitute() 
{
    std::cout << "I have no money left.\n";
}

DEBUGGER ERROR OUTPUT:

  •   _Mypair <struct at NULL>    std::_Compressed_pair<std::allocator<std::unique_ptr<Animal,std::default_delete<Animal>>>,std::_Vector_val<std::_Simple_types<std::unique_ptr<Animal,std::default_delete<Animal>>>>,1>
    
  •   _Mypair._Myval2 <struct at NULL>    std::_Vector_val<std::_Simple_types<std::unique_ptr<Animal,std::default_delete<Animal>>>>
    
  •   this    0x00000000 <NULL>   std::vector<std::unique_ptr<Animal,std::default_delete<Animal>>,std::allocator<std::unique_ptr<Animal,std::default_delete<Animal>>>> *
    
7
  • 3
    Undefined Behaviour sometimes leads to errors far away from where the issue actually is. Your prolem is that you use a moved-from unique_ptr (after move, unique_ptr stores nullptr): for (auto& animal : f->animals) Commented Mar 31, 2022 at 21:57
  • 2
    As I mentioned under your last question, you need to use std::move here, but you also need to understand first what std::move does, otherwise you will end up having problems such as here. See for example What is std::move(), and when should it be used? and What is move semantics? Commented Mar 31, 2022 at 21:59
  • 1
    To your credit. And for us to understand your question, I really like your Farm class =) Commented Mar 31, 2022 at 22:19
  • Thanks all. I've modified things slightly, I can now see that my "f" unique_ptr has a valid memory address by printing it out (cout<<&f). However when I try to add this (non-nullptr) unique_ptr to the "farms" vector in the "Coutryside" class, using push_back(std::move(f)) I get an error stating that I'm "attempting to reference a deleted function" - something to do with "default delete" Commented Mar 31, 2022 at 22:25
  • 2
    @NA &f is the address of the unique_ptr, not the address of the object stored in it. You need f.get() instead. If you have a compilation error, then please add the full error message to the question. Your question gives the impression that you are asking about a runtime error, not a compilation error. Commented Mar 31, 2022 at 22:31

1 Answer 1

1

Hi the problem is you are deferencing a null pointer in the for loop (f is set to nullptr). The move operation moves the ownership of the item pointed to and the pointer f is set to nullptr (move semantics) After a move the object can no longer be used

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.