• Revista PROGRAMAR: Já está disponível a edição #53 da revista programar. Faz já o download aqui!

merlin3000

[C++] Ajuda: Problemas com construtor de classes

19 mensagens neste tópico

Já mexo com o c++ há algum tempo.. mas só recentemente peguei novamente (e para não parar espero) sem ser para trabalhos no c++ e para aprender mais um pouco sobre alocação de memória, classes e outras coisas. Resolvi fazer uma classe simples de manipulação de arrays de caracteres tipo a string.h (muito mais simples claro).

Mas pelo caminho deparei-me com um problema. O código necessário para o exemplo é este.

-----------

main.cpp

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

using namespace std;

int main()
{
    CDstring a( "Texto 1" );
    CDstring f = a;
    CDstring l = "Texto 2";

return 0;
}

CDstring.h

#ifndef _CDstring_h_
#define _CDstring_h_

class CDstring
{
   int text_size(char *text, char lim='\0');

public:

   char *string;
   int last;


   CDstring();
   CDstring(CDstring &str);
   CDstring(char *text, char lim='\0');
   ~CDstring();
};
#endif

-----------

O erro acontece na linha a CDstring l = "Texto 2"; quando tento compilar.. aparece:

main.cpp    10  error: no matching function for call to `CDstring::CDstring(CDstring)'

CDstring.h  16  note: candidates are: CDstring::CDstring(char*, char)

CDstring.h  15  note:                          CDstring::CDstring(CDstring&)

main.cpp    10  error: initializing temporary from result of `CDstring::CDstring(char*, char)'

Não consigo perceber porque é que ele vê o "Texto 2" como um objecto do tipo CDstring! Se o construtor CDstring::CDstring(CDstring &str) não existir (e consequentemente a linha CDstring f = a comentada) funciona bem.. CDstring l = "Texto 2" chama o construtor CDstring(char *text, char lim='\0')..

A questão é que no VC++ Express (estou a programar no gcc com o Code::Blocks(Nightly Build 12-12-06)) funciona tudo bem, corre como seria esperado. Pelo menos como eu esperava que corresse lol...

Alguém me podia dar uma ajudinha? Não consigo perceber o que se passa já vi alguns tutoriais e a inicialização por "ObjectoX obx = Valor" devia ser permitida (descobri isso por acidente quando fiz o overloading do operador =  :-[

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Não sei se já tinha sido desejado, mas bem-vindo ao fórum =)

A ideia com que fico é que o compilador fica "um pouco confuso" porque não tens um construtor que receba só um char*, e quando inicializas uma CDstring com = ele procura por esse construtor e não encontra. Para tirar as dúvidas, experimenta alterar o construtor

CDstring(char *text, char lim='\0');

para

CDstring(char *text);

e vê se compila bem, o que vai acontecer quase de certeza.

Se assim for, para contornar o problema, utiliza 2 construtores:

CDstring(char *text, char lim);
CDstring(char *text);

em que a implementação do 2º é simplesmente

CDstring(char *text) {
CDstring(text, '\0');
}

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Já alguém tinham dito algo no post de apresentação lol mas Obrigado e obrigado pela resposta.

Em principio não seria disso porque o construtor tem o parametro lim pré-definido para '\0'. A linha  CDstring a( "Texto 1" ); funciona sem problemas. Mas tentando:

main.cpp:10: error: no matching function for call to `CDstring::CDstring(CDstring)'

CDstring.h:17: note: candidates are: CDstring::CDstring(const char*, char)

CDstring.h:16: note:                CDstring::CDstring(char*)

CDstring.h:15: note:                CDstring::CDstring(CDstring&)

main.cpp:10: error:  initializing temporary from result of `CDstring::CDstring(const char*, char)'

Apenas acrescentou mais ao construtor aos candidatos.. não tou a ver porque seja.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Para inicializares o objecto com o sinal de igual, creio que terás de definir o operador = na classe, pois caso contrário o compilador não sabe o que é que tu queres fazer com o sinal.

Penso eu de que... :hmm:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Mas nesse caso também não seria preciso declarar o operador = para que isto funcionasse? CDstring f = a; ... eu sei que neste caso não é. Pelo que tenho lido isto é o funcionamento normal para um copy constructor.

Mas inda só vi exemplos em que se usa o mesmo tipo de dados.. será que se for diferente é preciso o overload do operador =?

E a minha dúvida principal é que se o constructor CDstring(CDstring &str); não existir CDstring l = "Texto 2"; chama o constructor certo.

Em VC++ mesmo com os 2 constructores cada um é chamado na sua vez sem problemas.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ok.. noutro exemplo:

#include <iostream>

using namespace std;

class T
{
public:
    char data_[10];
    T(char *i) { cout<<"T(char *i)"<<endl; }
    T(T&) { cout<<"T(T&)"<<endl; }
};
int
main(int, char**)
{
    T t1("we"); // OK
    T t2 = "we"; // OK
    T t3(t2); //OK
    T t4 = t1; //OK
}

Aqui dá o mesmo erro mas se pusermos em vez de T(T&) { cout<<"T(T&)"<<endl; }

tivermos ... T(const T&) { cout<<"T(T&)"<<endl; } já funciona. Eu não percebo muito bem o funcionamento do const. Alguém me podia dizer o que é que ele ali faz de diferente? Isto aplicado ao exemplo anterior também funciona. Embora depois aja um erro dentro da função em si... mas o importante é que entra onde deve.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Aqui dá o mesmo erro mas se pusermos em vez de T(T&) { cout<<"T(T&)"<<endl; }

tivermos ... T(const T&) { cout<<"T(T&)"<<endl; } já funciona. Eu não percebo muito bem o funcionamento do const. Alguém me podia dizer o que é que ele ali faz de diferente? Isto aplicado ao exemplo anterior também funciona. Embora depois aja um erro dentro da função em si... mas o importante é que entra onde deve.

O const diz ao compilador que o argumento T não é modificado dentro da função, se tentares alterar algum argumento definido como const, o compilador deveria dar erro.

Sobre o teu código anterior (o primeiro), eu testei no VC++ 6.0 e funcionou perfeitamente...  :P

Cumpr. bk@ero  :thumbsup:

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Pois em VC++ eu também corro perfeitamente. O mal é que dá o tal erro no GCC e não sei porquê.

Acho que percebi o const mas.. porque faz difenreça neste caso então? não devia ser opcional para mim usar ou não? ali é como se ele o exigisse.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Correctamente o construtor copia deve ser sempre const. Não é lógico ao fazer uma cópia mudar o que se está a copiar.

E parece que o gcc valida isso.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Confirma-se. Dá o mesmo erro com o gcc em Linux. E aqui encontramos isto:

Copy constructor syntax

The copy constructor takes a reference to a const parameter. It is const to guarantee that the copy constructor doesn't change it, and it is a reference because a value parameter would require making a copy, which would invoke the copy constructor, which would make a copy of its parameter, which would invoke the copy constructor, which ...

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Hmm.. OK.. Obrigado pelas respostas.. já começo a a perceber.

Mas porque é que se não puser o const no construtor que leva o objecto em si, não dá erro na linha que deve chamar esse contrustor mas sim na linha que chamava o construtor que recebe um char* ?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Isto é para o primeiro exemplo.

O VC++ consegue criar alguns construtores, o que me parece que te esta a acontecer é que quando fazes CDstring l = "Texto 2"; é diferente de fazer isto CDstring l (Texto 2)";.

Exprimenta fazer esta alterçao ao codigo

    CDstring l;

    l=("texto 2");

diz o que acontece.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Nesse caso tenho que fazer o overload do operador =. Mas isso eu sei que funciona porque tenho já o fiz. (não pus no exemplo porque no VC++ ele ia buscar o construtor certo e no GCC quando não existe o construtor CDstring(CDstring &str); ele também chama o construtor correcto e em nenhum caso tenta chamar o operador = , ele queixa-se é de supostamentenão existir o construtor certo).

Em VC++ ele não cria um construtor chama o construtor CDstring(char *text, char lim='\0');

Em GCC por alguma razão queixa-se se não tiver const no construtor CDstring(CDstring &str); mas o estranho (para mim :/ pelo meno ) é que ele queixa-se na linha CDstring l = "Texto 2"; que chama o outro construtor. Não percebo porque é que interfere.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O comportamento é de prever olhando para os primeiros erros que apresentaste.

O compilador GCC na inicialização tem de chamar um construtor independentemente se existir o operador=, porque isto não é um construtor.

Logo o = na inicialização refere-se a um construtor que tenha só um parametro. O unico existe não tem const e logo não está correcto.

Agora porque é que ele não considera o construtor que tem o 2º parametro por defeito parece ser um comportamento caracteristico do GCC que difere do VC++. Qual é o comportamento correcto? Só vendo no standart c++

Na minha opinião o VC++ parece ter um comportamento mais logico, visto que existe uma opção para resolver o problema.

Embora ache que o facto de mantermos as coisas explicitas pode retirar confusão em código complexo. logo nesse aspecto o GCC ajuda a manter o código mais explicito.

Basicamente o código T a = x; é um alias de T a(x);

Para não existir confusão é sempre melhor usar T a(x);

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

O mal é que mesmo que exista o construtor CDstring(char *text); ele dá o mesmo erro como tá num exemplo ali em cima. O que tava a tentar descobrir é porqe é que usar o = na declaração dá erro mas chamar no estilo de Objecto Ob("Texto" ) funciona bem.

Deve existir alguma diferença eu é que não tou a ver qual lol

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

A diferença está em que ao usares o = ao construir o objecto, o compilador procura pelo construtor por cópia, que exige que o parâmetro passado seja const. Ao chamares o construtor explicitamente e passares um objecto que não é const já não há essa restrição. É a mesma diferença entre este código:

	CDstring a( "Texto 1" );
CDstring f(a);

e este:

	const CDstring a( "Texto 1" );
CDstring f(a);

em que o 2º troço só funciona se tiveres um construtor que receba um const CDString, e não funciona se só tiveres um construtor que receba um CDString não const.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Ok.. acho que começo a perceber.. desculpem lá se tou a ser chato.

O que eu não entendo é o que é que o CDstring = "Texto 1" tem a ver com o construtor CDstring(CDstring &str). Se esse construtor não exisitir ele chama o construtor CDstring(char *text, char lim='\0') e se tiver o const no em  CDstring(CDstring &str) também chama CDstring(char *text, char lim='\0'). Se ele sabe qual chamar porque precisa que exista um construtor com const que ele não vai usar.

E por exemplo: CDstring f = a; isto também não deveria dar o mesmo problema? FUnciona direito mesmo sem ter const no construtor cópia.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Olhando de novo para o código, e em particular para este erro:

main.cpp    10  error: initializing temporary from result of `CDstring::CDstring(char*, char)'

quando fazes

    CDstring l = "Texto 2";

o que acontece é que o compilador está a criar uma CDstring temporária com o construtor que recebe um char*, e depois cria o objecto l por cópia dessa CDstring temporária... por isso é que se está a queixar do construtor por cópia.

Agora o porquê de isso acontecer... imagino que seja por não teres um construtor que receba exclusivamente um char*. Podes, como disse antes, experimentar esse código sem o construtor CDstring(char *test, char lim='\0'), e no seu lugar dois construtores: um CDstring(char *test, char lim) com exactamente o mesmo código do anterior, e outro simplesmente

CDstring(char *test){ CDstring(test, '\0'); }

para ver o que acontece então. Se estiver certo, será chamado este último construtor, que irá chamar o outro.

Já do CDstring f = a; não se queixa porque existe um construtor que recebe um parâmetro do tipo de a. CDstring f = a; não chama o "construtor por cópia", chama um construtor que receba um parâmetro do tipo de a.

Já agora, por definição, o construtor por cópia recebe um parâmetro const. Se o parâmetro não for const, não é um construtor por cópia.

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Obrigado pela explicação TheDark.

Com o construtor CDstring(char *test) e sem o const no CDstring(CDstring &str) simplesmente adiciona mais um construtor aos candidatos. Com o const funciona bem. Mas acho que percebi a ideia. Se declarar com o = com um tipo que não seja o do objecto em si ele chama o construtor cópia certo?

Mas no debug passo a passo não devia passar por lá? Quando se faz debug ele simplesmente entra no construtor  CDstring(char *text, char lim='\0'), mas não se vê a passar no construtor cópia. E quando o construtor CDstring(CDstring &str) não existe? Ele funciona bem, ele consegue trabalhar sem esse construtor?

0

Partilhar esta mensagem


Link para a mensagem
Partilhar noutros sites

Crie uma conta ou ligue-se para comentar

Só membros podem comentar

Criar nova conta

Registe para ter uma conta na nossa comunidade. É fácil!


Registar nova conta

Entra

Já tem conta? Inicie sessão aqui.


Entrar Agora