Jump to content

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


Recommended Posts

Posted

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 =  ?

Criar é Divertido

Posted

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');
}

Desaparecido.

Posted

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.

Criar é Divertido

Posted

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... ?

Posted

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.

Criar é Divertido

Posted

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.

Criar é Divertido

Posted

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...  😛

Cumpr. bk@ero  👍

Posted

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.

Criar é Divertido

Posted

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.

Aqui há coisa de 2 anos fazia umas malhas de croché, depois fartei-me e fui para informática!

Posted

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 ...

Desaparecido.

Posted

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* ?

Criar é Divertido

Posted

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.

Posted

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.

Criar é Divertido

Posted

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);

Aqui há coisa de 2 anos fazia umas malhas de croché, depois fartei-me e fui para informática!

Posted

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

Criar é Divertido

Posted

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.

Desaparecido.

Posted

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.

Criar é Divertido

Posted

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.

Desaparecido.

Posted

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?

Criar é Divertido

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.