Assign one class to another in C++

Asked

Viewed 3,232 times

1

There is a class as below:

class vetor
{
protected:
    int x, y;
public:
    //Construtor
    vetor(int _x=0, int _y=0);
};

and another:

class pos : public vetor
{

public:
    //Construtor
    pos();

    umaFuncaoQualquer(int _x=0, int _y=0);
    outraFuncaoQualquer(int _x=0, int _y=0, bool algo=false);
    maisUmaFuncaoQualquer(void);
};

I want to do this:

vetor v(1, 3);
pos p;
p = v;

How would I do that in c++? Would I need to create a function for that, or am I thinking wrong?

Up 1:

I managed to do it this way, in the vector class:

class vetor
{
protected:
    int x, y;
public:
    //Construtor
    vetor(int _x=0, int _y=0);

    //Retorna x
    int getX(void);
    //Retorna y
    int getY(void);
};

in the pos class:

class pos : public vetor
{

public:
    //Construtor default
    pos();
    /*
    Construtor para receber o vetor. É responsável por atribuir a x e y 
    herdados pela classe vetor o x e y de uma instância vetor atribuída, 
    utilizando as funções getX e getY da classe vetor, 
    que pega o x,y que estão protected na mesma.
    Sua implementação:
    pos::pos(vetor _v)
    {
        x = _v.getX();
        y = _v.getY();
    };
    */
    pos(vetor _v);

    umaFuncaoQualquer(int _x=0, int _y=0);
    outraFuncaoQualquer(int _x=0, int _y=0, bool algo=false);
    maisUmaFuncaoQualquer(void);

};

with that the code below works:

vetor v(1, 3);
pos p;
p = v;

However I would like to know other ways to do the same, maybe there are ways that fit better in this situation. And I didn’t understand very well why it works this way and not the first way if they both have x and y. I remember reading about it once:

//Funciona sem precisar do construtor de cópia
v = p;
//Apenas funciona se tiver um construtor de cópia
p = v;

I read it like that, but I can’t remember why?

Up 2:

Referring to @Ossetian_odin’s reply, I’m still not understanding some points, I’ll start from top p low:

1 - Regarding the use of Friend, it would not be necessary in the case of my example, because the members of the class "vector" are protected and not private, so they can already be accessed by the derived class. And the copy constructor that the compiler declares, is in the class itself receiving the class itself, as I want to assign a different one, requires me to create a constructor for that, the problem is that I created in the class "pos" and not in the "vector", which is somewhat confusing, but it could not be the opposite, since following the logic "is one", "vector" is not a "pos", but "pos" is a "vector". I’m sure?

2 - As for "downcasting", you say to create an abstract class, so would I create the "vector" class as abstract? Wouldn’t it be better to use "dynamic_cast" in this case? And if I use "dynamic_cast" what happens when I do "dynamic_cast < pos * > (&v);", p will have all the variables and functions of "pos"(since it inherits "vector", are those of "vector" and those that only exist in "pos"), receiving the assignment of v the variables that have in common, or p will have only what "vector" has?

Up 3:

I studied it, and I realized the way I Up 2 is the right one for this case, at least the most certain I’ve found so far. Because what I want to do is to assign a "vector" object to a "pos" object in the most "abstract" way possible, abstraction for another person who will use, it is easier to do "p = v;" than "static_cast(&vet);" for example; what I need is not a "downcasting", and not to use polymorphism, but I appreciate both responses that made me study these concepts further, because what is really needed in this case is simply to assign "p = v", where p having a constructor to handle the assigned class will work, it’s not conversion(Polymorphism) It needs a copy. I read about a material I found in Google, and in the chapter "Pointer Conversion" explained to me:

https://webserver2.tecgraf.puc-rio.br/ftp_pub/lfm/CppBorgesClinio.pdf

Translating for the variables of the question:

 vetor v, *pv; // pv pode apontar para objetos do tipo vetor e derivados
 pos p, *pp; // pp pode apontar para objetos do tipo pos e derivados

 v = p; // copia v parte vetor de p para v (não é conversão)
 p = v; /* erro! v pode não ter todos elementos para a cópia(A questão se 
           refere a este caso, que também não é conversão, e pode ser 
           resolvido da maneira do Up 2, o que queria saber era se existe
           outras maneiras alem da que fiz no Up 2, que seria para cópia, 
           e não mudança de forma ou tratar um classe como uma 
           diferente)
        */

 pv = &v; // ok
 pv = &p; // ok, pv aponta para um objeto do tipo pos
 pp = pv; // erro! pv pode apontar para um objeto do tipo vetor
 pp = &p; // ok
 pp = &v; // erro! pp não pode apontar para objetos do tipo vetor 

2 answers

1

You are trying to treat a mother class object as an object of the daughter class. It does not work.

Concrete example

Imagine you have the classes animal, gato (inherited from an animal) and urocordato. A animal has the method comer. A gato also has the methods miau, serFofo and ignorarDono. A urocordato has also the methods metamorfose, filtrar and brotamento.

Imagine that for your algorithm you need a animal, let’s call this animal estimacao:

animal estimacao;
estimacao = new gato();
// faz trabalho com um animal do tipo gato
estimacao = new urocordato();
// faz trabalho com animal do tipo urocordato

There is no problem with the above algorithm. Now, imagine that we have a function that receives a gato as argument. Let’s call this function mimar, your signature is void mimar(gato xano);. See what happens below:

//declaração e objeto do tipo gato, tudo certo
gato xaninho = new gato();
mimar(xaninho);

// declaração de animal, objeto gato; possível atribuir, mas não possível chamar 'mimar'
animal bixo = new gato();
mimar(bixo);

// declaração de gato, objeto urocordato; impossível atribuir
gato xaninho = new urocordato();
mimar(xaninho);

Why?

When you work with inheritance, you can assume that urocordato and gato can be treated like animals. The definition of classes speaks exactly this through inheritance:

  • class gato class-derived animal: means that gato is a specific type of animal
  • class urocordato class-derived animal: means that urocordato is a specific type of animal

But you can’t treat a animal generic as a gato. When you do gato xaninho = new urocordato(); you are trying to treat a animal any one like gato.

If it were possible to treat a animal any one like gato, it would be possible to call the method miau() of a urocordato (for example, in the implementation of void mimar(gato xano)), whereas urocordatoare not able to do miau().

UPDATE

Automatic construction

If at some point, you declare a class constructor with only a single argument object, you can assign the argument to the class variable. Internally, C++ will create a target class object automatically by calling the appropriate constructor. This has nothing to do with inheritance.

Example without using relation between gato and animal:

int giid = 0;

class animal {
public:
    int id;
    int iid;
    animal(int _id = 0) {
        cout << "construindo um animal..." << endl;
        id = _id;
        iid = giid++;
    }

};

class gato {
public:
    int id, iid;
    gato(animal a) {
        id = a.id;
        iid = giid++;
        cout << "construtor chamado automaticamente, meaw" << endl;
    }
};

//códigos...
animal a;
gato xano = a; // chama o construtor de gato passando um animal

cout << "instance id do animal " << a.iid << endl;
cout << "instance id do xano " << xano.iid << endl;
//códigos...

See working on ideone.

Exit from execution:

building an animal...
automatically called constructor, meaw
instance id of animal 0
instance id of xano 1

I also set another example that, yes, made the confusion:

int giid = 0;

class animal {
public:
int id;
int iid;
animal(int _id = 0) {
cout << "construindo um animal..." << endl;
id = _id;
iid = giid++;
}

};

class gato: public animal {
public:
gato(animal a) {
id = a.id;
cout << "construtor chamado automaticamente, meaw" << endl;
}
};

// códigos...
animal a;
gato xano = a;

cout << "instance id do animal " << a.iid << endl;
cout << "instance id do xano " << xano.iid << endl;
// códigos...

See working on ideone

Exit from execution:

building an animal...
building an animal...
automatically called constructor, meaw
instance id of animal 0
instance id of xano 1

  • And because when I create a works constructor, I wouldn’t be assigning?

  • Is there a way to put a code snippet with doubt? Type gato xaninho = new urocordato()?

  • An example of the constructor in Up 1 is in the question.

  • But what he was asking using his serial example something like this: why animal bixo = new gato(); works in C++, but gato gatinho = new animal(); doesn’t work? but it works if I create a copy builder to handle the members of "animal" and assign them to the members of "cat"?

  • @I’ll study before I update my answer. It seems that if he creates a constructor that receives an object and tries to assign that object to his, he knows how to do the coercion

  • I think it’s called a copy builder, but I’ve never quite understood how C++ handles it.

  • @Perdugames, copy builder I remember in another scope, but anyway. I did the example: http://ideone.com/ir5d46. I will update my answer

Show 2 more comments

1

I don’t understand the purpose/logic of your code. I think what you want to do is called "downcasting", which is only possible through pointers and references:

#include <iostream>
using namespace std;


class vetor
{
protected:
    int m_x;
    int m_y;

public:
    vetor(int x = 0, int y = 0)
    {
        m_x = x;
        m_y = y;
    }

    void imprime()
    {
        cout << "imprimindo como \'vetor\'";
        cout << "\nx: " << m_x;
        cout << "\ny: " << m_y << "\n\n";
     }
};


class pos : public vetor
{
public:
    pos(int x, int y) : vetor(x, y) {} //chama o construtor da classe base

    void imprime()
    {
        cout << "imprimindo como \'pos\'";
        cout << "\nx: " << m_x;
        cout << "\ny: " << m_y << "\n\n";
    }
};


int main()
{
    vetor vet(10, 20);
    vet.imprime();

    pos* ps = static_cast<pos*>(&vet);
    ps->imprime();

    return 0;
}

However, using a pointer/reference from a class derived to a base object is not a good practice of programming, because it will only work if the layout of the base and derivative classes are equal in addition to leaving your program with a confusing logic, breaking the class hierarchy. And it wouldn’t work if you were using virtual functions either.

The right thing would be for you to create an abstract base class and use it to reference derived objects, using the "is one" logic, which @Jefferson Quesado explained:

#include <iostream>
#include <memory>
using namespace std;


class Poligono //classe abstrata
{
public:
    virtual void desenha() = 0; //função virtual pura
    virtual ~Poligono() {} //destrutor é declarado virtual para que seja chamado quando objeto 
     //filho for destruído
};


class Triangulo : public Poligono
{
public:
    void desenha() override //implementa função virtual
    {
        cout << "Desenha triangulo.\n";
    }
};


class Retangulo : public Poligono
{
public:
    void desenha() override
    {
        cout << "Desenha retangulo.\n";
    }
};


void FacaAlgumaCoisa(Poligono& pol)
{
    pol.desenha();
}


int main()
{
    unique_ptr<Poligono> pol; //ponteiro da classe base abstrata

    pol = make_unique<Triangulo>(); //usa pol como triângulo
    pol->desenha();

    pol = make_unique<Retangulo>(); //destroi triângulo e usa pol como retângulo
    pol->desenha();

    //usa uma função para obter o mesmo efeito acima
    FacaAlgumaCoisa(Retangulo());
    FacaAlgumaCoisa(Triangulo());

    return 0;
}

About the copy constructor, it is the constructor you define to determine the behavior of your class when it is assigned:

class Pessoa
{
    string nome;
    int idade;

public:
    //construtor pra inicializar
    Pessoa(const string& nome, int idade)
    {
        this->nome = nome;
        this->idade = idade;
    }

    //construtor de cópia
    Pessoa(const Pessoa& p)
    {
        cout << p.nome << " esta sendo copiado(a)!\n";
        nome = p.nome; //se os objetos fore do mesmo tipo, vc pode acessar os membro privados
        idade = p.idade;
    }
};

Using the class:

Pessoa p1("John", 30);
Pessoa p2 = p1; //chama o construtor de cópia
Pessoa p3(p1); //faz a mesma coisa que a linha acima

You can also use the copy constructor of a class to receive another type of object (as you did, so your code worked when you assigned two classes of different types), but unless I am mistaken, you will need to mark the class as "friend" the other object with the keyword friend, if the two classes have no relationship. And another detail is that if you don’t declare the copy constructor, the compiler will declare one for the same type implicitly, and its default behavior is to call the assignment operator on all members of your class.

EDIT:

Answering the other questions:

1 - In the case of your code, you do not need to declare Friend because the derived class has access to the protected fields of the base class. And no, the compiler generates the copy constructor implicitly in all classes that you do not declare and implement it. When you call the copy constructor, obviously the default constructor of the base class is also called. I recommend that you create a base class and a derivative and run tests marking all constructors with std::cout for you to better understand how it works:

struct Base
{
    Base()
    {
        cout << "base created!\n";
    }

    Base(const Base& b)
    {
        cout << "copy constructor base\n";
    }
};

struct Child : Base
{
    Child()
    {
        cout << "child created!\n";
    }

    Child(const Child& c)
    {
        cout << "copy constructor child\n";
    }
};

2 - "Downcasting" is when you try to use a derived class as a base class, which is the opposite of the second example I showed. The base class should be used as a pointer/reference, since derivatives inherit all the characteristics of the base, and the opposite does not occur. And yes, "vector" should be abstract, but in this case it works because it is identical to the class "pos". And dynamic_cast serves the same purpose as a static_cast normal, the difference is that this cast checks at runtime if the conversion is valid and returns a null pointer if it is invalid, and only works with classes with methods marked as virtual. In the first example of my post, if you tried to mark the "print" method as virtual, and replace static_cast for dynamic_cast, the conversion would fail for the reasons I have already mentioned (and also because virtual functions function differently from common methods). Research class hierarchy and polymorphism to better understand these concepts.

  • I edited the question "Up 2", it was too big to comment here.

  • When I said "in the class itself", I was referring to this "Base(const Base& b)", the compiler does this implicitly, not this "Base(const Child& c)" which is what I think I want, I say I think, because I might want something wrong, but what I really want is only that when I assign a "vector" object to a "pos", that it takes the members in common(x,y, because "pos" has x,y inherited from "vector" that has x,y, and those two classes would be different, just having those members in common) and do the attribution.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.