2

Tengo un atributo en mi clase Book llamado author. Queda es referencia.

 Author & author;

Y tengo mi constructor:

Book::Book(const string & name, const Author & auth, double price, int    qtyInStock)
{
  this->name=name;
      this->author=author;
  setPrice(price);
  setQtyInStock(qtyInStock);
}

Mi problema viene al inicializar la referencia author, atributo de la clase, en el constructor de Book. Yo pensaba que se llamaba al constructor por defecto y luego al constructor de igualdad ambos de la clase Author. Ya que author no esta inicializado.

Author::Author(const Author & refAuthor){

   email=refAuthor.email;
   gender=refAuthor.gender;
   name=refAuthor.name;
}

Author & Author::operator =(const Author &refAuthor){

    email=refAuthor.email;
    gender=refAuthor.gender;
    name=refAuthor.name;
    return *this;
}

Y el objeto devuelto se asignaba a mi referencia. Porque este código no compila? El constructor devolvería un objeto Author que asignaría a mi referencia. También probé a definir const Author & author ya que el parámetro pasado al constructor es una referencia const. Pero sigue sin compilar. NO QUIERO RESOLVERLO INICIALIZANDO LOS PARAMETROS EN LA LISTA DEL CONSTRUCTOR.

g++ -O0 -g3 -Wall -c -fmessage-length=0 -o Book_test.o ..\Book_test.cpp
g++ -O0 -g3 -Wall -c -fmessage-length=0 -o Book.o ..\Book.cpp
..\Book.cpp: In constructor 'Book::Book(const string&, const Author&,   double, int)':
..\Book.cpp:9:1: error: uninitialized reference member in 'class Author&' [-fpermissive]
Book::Book(const string & name, const Author & auth, double price, int qtyInStock)
^~~~
In file included from ..\Book.cpp:3:0:
..\Book.h:12:13: note: 'Author& Book::author' should be initialized
Author & author;
         ^~~~~~
Build error occurred, build is stopped
Time consumed: 1409  ms.  

Si defino const Author & me devuelve el siguiente fallo:

 **** Build of configuration Debug for project Author ****

 **** Internal Builder is used for build               ****
 g++ -O0 -g3 -Wall -c -fmessage-length=0 -o Book.o ..\ 
 ..\Book.cpp: In constructor 'Book::Book(const string&, const Author&, double, int)':
 ..\Book.cpp:9:1: error: uninitialized reference member in 'const class Author&' [-fpermissive]
 Book::Book(const string & name, const Author & auth, double price, int qtyInStock)
     ^~~~
    In file included from ..\Book.cpp:3:0:
    ..\Book.h:12:19: note: 'const Author& Book::author' should be initialized
    const Author & author;
                             ^~~~~~
     ..\Book.cpp:12:15: error: passing 'const Author' as 'this' argument   discards qualifiers [-fpermissive]
     this->author=author;
                           ^~~~~~
      In file included from ..\Book.h:6:0,
         from ..\Book.cpp:3:
      ..\Author.h:23:13: note:   in call to 'Author& Author::operator=(const    Author&)'
       Author & operator=(const Author &refAuthor);
                     ^~~~~~~~
       Build error occurred, build is stopped
       Time consumed: 765  ms.  

Muchas gracias, por adelantado.

Jcpardo
  • 435
  • 2
  • 8
  • Puedes mostrar la clase Book? Tengo el presentimiento que el problema está en cómo declaraste author en la definición de Book – JACH Apr 26 '18 at 04:05

2 Answers2

1

Tengo un atributo en mi clase libro llamado author.

Author & author;

Ya empezamos mal. No tienes un atributo sino una referencia... fíjate en ese &. Las referencias pueden ser utilizadas como miembros de una clase, pero deben inicializarse sí o sí en la parte de inicialización del constructor:

Book::Book(const string & name, const Author & auth, double price, int    qtyInStock)
  : author(author) // <<--- AQUI
{
  this->name=name;
  //this->author=author; <<--- esto sobra
  setPrice(price);
  setQtyInStock(qtyInStock);
}

Ahora bien, una referencia no implica una copia de objetos, luego ese constructor tiene un problema y es que estás intentando asignar una referencia constante a una referencia que no es constante... luego el programa va a seguir sin compilar...

Lo normal en estos casos es que author deje de ser una referencia... así en vez de copiar una referencia se realizará una copia del objeto y el programa compilará sin problemas:

class Book
{
  Author author; // sin &
};

NO QUIERO RESOLVERLO INICIALIZANDO LOS PARAMETROS EN LA LISTA DEL CONSTRUCTOR.

Si quitas la referencia podrás inicializarlo donde te de la gana (siempre y cuando Author implemente de forma implícita o explícita el constructor por defecto)... no obstante piensa que inicializar los miembros dentro de las llaves del constructor tiene cierto impacto en el código ya que primero se va a llamar al constructor por defecto de cada miembro y después al operador de asignación... en vez de únicamente al constructor copia... es un dos por uno.

eferion
  • 49,291
  • 5
  • 30
  • 72
  • @Jcpardo lo siento pero no entiendo tu comentario... está incompleto – eferion Apr 26 '18 at 10:45
  • buenas, pero aunque yo defina author & author, se que es una referencia, pero sigue siendo un atributo de la clase libro no? en términos prácticos no afecta, sólo lo que comentas que también lo probé para ver si compilaba que fue declarar el atributo en lugar de Author & author lo declaré como const Author & author, pero sigue saliendo error de compilación. – Jcpardo Apr 26 '18 at 10:46
  • @Jcpardo sigue siendo un miembro... pero al ser una referencia tu objeto no va a ser el propietario... y nombrar atributo a algo que no es tuyo... me chirría un poco – eferion Apr 26 '18 at 10:48
  • Ya cambie la pregunta inicial. A ver si me puedes decir cómo puedo hacer, o que hago mal si lo hago sin inicializar en la lista del constructor. Ya que en la igualdad se haría una llamada al constructor y me devolvería un objeto. Este objeto tendría la misma dirección q la del parámetro pasado o distinta? No veo dónde falla código – Jcpardo Apr 26 '18 at 14:41
  • @Jcpardo Como te comenté en la respuesta hace unas once horas... si **no quieres usar la inicialización del constructor**, es decir, el espacio que sigue a los dos puntos, **no puedes** usar referencias, ya sean constantes o no constantes, ya que las mismas hay que inicializarlas en su construcción... y eso solo es posible hacerlo detrás de los dos puntos del constructor – eferion Apr 26 '18 at 16:32
  • Vale. Cierro caso – Jcpardo Apr 26 '18 at 17:13
  • Marcado, Y gracias por toda la ayuda prestada. – Jcpardo Apr 27 '18 at 19:29
1

Las referencias en C++ son un tipo de datos que apunta a otro objeto existente y tras definirse no pueden apuntar a otro objeto, a efectos prácticos la referencia y el objeto referenciado son el mismo objeto. Puedes ver todos los detalles en este hilo.

Por la naturaleza y comportamiento de las referencias, es imposible crear una referencia que no apunte a nada:

struct Author {};

int main() {
    // error: 'author' declarado como referencia pero no es inicializado
    Author & author;

    return 0;
}

Por otro lado, por la naturaleza y comportamiento de los objetos en C++, dentro del cuerpo del constructor de un objeto, el ciclo de vida de sus sub-objetos ya ha empezado, por lo que los sub-objetos no reasignables no pueden obtener un nuevo valor:

struct Book {
    // error: la declaración de la referencia 'author' requiere un inicializador
    Author & author;

    Book(Author &a) {
        author = a;
    }
};

Así que tengo malas noticias:

NO QUIERO RESOLVERLO INICIALIZANDO LOS PARAMETROS EN LA LISTA DEL CONSTRUCTOR

Lo siento, DEBES resolverlo inicializando los parámetros en la lista del constructor. No existe ninguna otra manera de inicializar una variable miembro de tipo referencia:

struct Author {};

struct Book {
    Author & author;

    Book(Author &a) : author(a) {
        /* Aquí ya ha empezado el ciclo de vida de 'author', por ello
        dado que las referencias obligatoriamente deben apuntar a algo
        antes de entrar en el cuerpo del constructor deben apuntar a algo. */
    }
};

int main() {
    Author a;
    Book b{a};
    return 0;
}
PaperBirdMaster
  • 44,474
  • 6
  • 44
  • 82