0

This is the method in which the error occurs. The goal here is to generate a certain number of sand grains based on a model grain (modele), for a given small increment of time dt. Grain is an abstract class because it hase purely virtual methods and it has to be that way. The problem appears on

for(size_t a(0); a<vect.size(); ++a){
    vect[a] = new Grain(*modele);
}

without this initializing loop, the vector returns all same grains but I need them to be slightly different (the Aleatoire class provides the necessary tool for that). For example if I do

for(size_t a(0); a<vect.size(); ++a){
    vect[a] = modele;
}

The error disappears but the vector returned contains all same grains, they are all the last grain from the setting loop

std::vector<Grain*> Source::creation( std::vector<Grain*> grains, double dt){
vector<Grain*> vect; //vide par defaut

Aleatoire generateur(1);

//determiner nombre de grains a generer: DEBUT ALGO
double fraction = debit*dt; // fraction = debit "vrai", mais a priori non entier
int nombre(fraction);      // partie entière
fraction -= nombre;        // partie fractionnaire
// on ajoute 1 au hasard, proportionnellement à la partie fractionnaire :
if ( generateur.uniforme(0.0, 1.0) < fraction ) ++nombre;
//---FIN ALGO---
cout<<"Pour un pas de temps "<<dt<<" on genere "<<nombre<<" grains."<<endl;

double x, y, z, r;
vect.resize(nombre, nullptr);

for(size_t a(0); a<vect.size(); ++a){
    vect[a] = new Grain(*modele);
}
cout<< "APRES LA FONCTION RESIZE, la taille du tableau est : "<<vect.size()<<endl;

for(size_t i(0); i<nombre; ++i){
    (vect[i])->setPosition(position); //Le grain genere est genere a la position de la source
    x = generateur.gaussienne(vitesse_iMoyenne.getCoord_x(), ecart_typeVitesse);
//        cout<<"x = "<<setw(2)<<x<<" ";
    y = generateur.gaussienne(vitesse_iMoyenne.getCoord_y(), ecart_typeVitesse);
//        cout<<"y = "<<setw(2)<<y<<" ";
    z = generateur.gaussienne(vitesse_iMoyenne.getCoord_z(), ecart_typeVitesse);
//        cout<<"z = "<<setw(2)<<z<<" ";
    Vecteur3D v(x,y,z);
//        cout<<"Le nouveau vecteur vitesse est : "<<setw(2)<<v<<" "<<endl;
    vect[i]->setVitesse(Vecteur3D(x,y,z));
//        cout<<"Le vecteur vitesse copie est : "<<setw(2)<<vect[i]->getVitesse()<<" "<<endl;

    r = generateur.gaussienne(modele->getRayon(), ecart_typeRayon);
//        cout<<"Le nouveau rayon est : "<<setw(2)<<r<<" "<<endl;
    vect[i]->setRayon(abs(r));
//        cout<<"Le rayon copie est : "<<vect[i]->getRayon()<<" "<<endl;

//        cout<<"Affichage dans la methode, dans la boucle for : "<<endl;
//        cout<<setw(2)<<i<<" "<<*(vect[i])<<endl;
//        cout<<endl;
}

//    cout<<"Affichage dans la methode du vecteur de grains generes : "<<endl;
//    for(size_t j(0); j<vect.size(); ++j){
//        cout<<setw(2)<<j<<" "<<*(vect[j])<<endl;
//    }
//    cout<<endl;
return vect;  
}

Here is Grain.hpp:

#ifndef Grain_hpp
#define Grain_hpp
#include "Vecteur3D.hpp"
#include "Dessinable.hpp"

class Grain : public Dessinable {
public:
Grain(Vecteur3D p={0.0,0.0,0.0}, Vecteur3D v={0.0,0.0,0.0}, double m =             1.0, double r = 1.0, Vecteur3D f = {0.0,0.0,0.0}, SupportADessin* support = nullptr );

virtual ~Grain();

double masse() const;

//eta_milieu est la constante de viscosite du milieu

double lambda() const; //coef de frottement fluide

void ajouteForce();

void bouger(double pas);

virtual void affiche(std::ostream& sortie) const ;

void setVitesse(Vecteur3D const& v);

void setPosition(Vecteur3D const& p);

void setRayon(double const& r);

Vecteur3D getPosition() const;

double getRayon() const;

Vecteur3D getVitesse() const;

Vecteur3D calcule_force() const;

virtual Vecteur3D* vecteurForce(Grain* grain) const =0;



//Methodes forces virtuelles

virtual void ajouteForce(Obstacle* obstacle) = 0;

virtual void ajouteForce(Grain* grain) = 0;

virtual void ajouteForce(Vecteur3D) = 0;

//Dessinable


Grain(SupportADessin* vue)
: Dessinable(vue)
{}

virtual void dessine() override;

protected:

Vecteur3D position;

Vecteur3D vitesse;

double m_volumique;

double rayon;

Vecteur3D force;

};
std::ostream& operator<<(std::ostream& sortie, Grain const& g) ;
#endif /* Grain_hpp */
John Cataldo
  • 105
  • 5
  • 1
    Why does `Grain` have pure virtual functions in it? – NathanOliver May 08 '17 at 18:21
  • 1
    The error is nothing if not self-explanatory. `Grain` is an abstract class. Your pure virtual members solidify that status. So... stop trying to make concrete instances of said-same. – WhozCraig May 08 '17 at 18:24
  • Thanks @WhozCraig for the comment. Well I know that I cannot make a concrete instance of an abstract class, but here I just mak a pointer to one. Nothing forbids doing heterogeneous collections of objects that are potentially different but belong to the same super class. Like a collection of vehicles that can later become planes cars submarines etc. – John Cataldo May 08 '17 at 18:28
  • 1
    @StanislasHildebrandt `new Grain(*modele);` creates concrete instance of it. – Algirdas Preidžius May 08 '17 at 18:30
  • Thanks @AlgirdasPreidžius I wasn't sure about that, I thought that the reserved word 'new' returned a pointer to wathever, is there maybe an alternative that could be used here? – John Cataldo May 08 '17 at 18:31
  • `= new Grain(*modele)` would create an instance of `Grain` (if this class were not abstract). Do you actually want to create a copy of `modele`? – Stephan Lechner May 08 '17 at 18:32
  • @StephanLechner yes a copy would be fine I guess, if the end result - getting a vector of slightly different grains, is the same – John Cataldo May 08 '17 at 18:34
  • @Stanislas Hildebrandt `new` creates an *object* (a concrete instance) on the free store and returns a pointer to it. – Jesper Juhl May 08 '17 at 18:37
  • @JesperJuhl is there a way to assign the value of a pointer to the object without creating the instance of it? – John Cataldo May 08 '17 at 18:39
  • @Stanislas Hildebrandt that doesn't make any sense. If there is no concrete object what would the pointer point to? A pointer is simply the address of an object. It's like a road sign saying "the town is *that* way". No object (town), nothing to point at. – Jesper Juhl May 08 '17 at 18:40
  • @JesperJuhl but I can make a whole vector of pointers to abstract objects, why can't I make one pointer to an abstract object? – John Cataldo May 08 '17 at 18:42
  • @Stanislas Hildebrandt a container of pointers of abstract types still requires all those pointers to point at concrete instances of *derived* type objects (at least if those pointers are going to be valid/useful). There has to be an actual object at the end of the pointer. – Jesper Juhl May 08 '17 at 18:44
  • If you just want a pointer variable of some abstract type that does not point to anything, then initialize it with `nullptr`. – Jesper Juhl May 08 '17 at 18:52
  • @JesperJuhl but that's the thing, due to some error that i've been trying to solve, (first of all nullptr makes seg faults) if I dont do this loop, the returned vector is one that contains all same grains (the last grain of the loop). – John Cataldo May 08 '17 at 19:04

1 Answers1

0

In your case, it seems that you want to clone an existing object. If you know the type of this object to copy, you'd use a copy constructor. Suppose the following abstract base class and a concrete subclass of it:

class AbstractBase {
public:
    virtual void print() = 0;
};

class ConcreteClass : public AbstractBase {
public:
    ConcreteClass(int initVal) : testVal(initVal) {};
    virtual void print() { cout << "ConcreteClass with testVal " << testVal << endl; }
    int testVal;
};

int main() {    
    ConcreteClass *cObj = new ConcreteClass(5);
    AbstractBase* copyOfCObj = new ConcreteClass(*cObj);
    copyOfCObj->print(); // output: ConcreteClass with testVal 5
    return 0;
}

Note that the code does not specify an individual copy constructor, so the compiler will generate a default copy constructor (which actually clones all data-members). If this is not sufficient, define your own copy constructor; but this is a separate topic.

If you do not know the concrete type of the object to clone (e.g. because you just have a pointer to it, and this pointer is assigned an object somewhere else), then some custom code is necessary (cf., for example, Copying a Polymorphic object in C++). C++ does - to my knowledge - not provide a built-in "clone"-functionality unless you know the concrete type of this object; so you have to provide your own "clone"-functionality which brings this type information with it:

class AbstractBase {
public:
    virtual AbstractBase* clone() = 0;
    virtual void print() = 0;
};

class ConcreteClass : public AbstractBase {
public:
    ConcreteClass(int initVal) : testVal(initVal) {};

    virtual ConcreteClass* clone() {
        return new ConcreteClass(*this);
    }

    virtual void print() { cout << "ConcreteClass with testVal " << testVal << endl; }

    int testVal;
};

int main() {
    AbstractBase* modele = new ConcreteClass(10);
    AbstractBase* copyOfModele = modele->clone();
    copyOfModele->print(); // output: ConcreteClass with testVal 10
    return 0;
}
Community
  • 1
  • 1
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • Thanks for the answer, unfortunately the thing that I'd wish to do, if it were possible, is a clone() method that also works with an abstract object. The method creation() is creating a vector of pointers to abstract objects according to a model of also an abstract object, hence the copy or clone of the abstract object... I don't know if what I'm trying to do is possible, if not I might consider making this methid specific to a certain concrete sub-object. But then the thing is that creation has to be able to create vectors of Grains according to different sub-cobjects of Grains... – John Cataldo May 08 '17 at 20:40
  • You can't achieve an instance of an abstract class, neither by copying, nor by cloning, nor by somehow else trying to create it. You can just create concrete objects, though a pointer of type `AbstractClass*` may point to them. Note that the object itself will remain a concrete one, even if a (polymorphic) pointer of type `AbstractClass*` points to it. – Stephan Lechner May 08 '17 at 20:45