Pergunta

Eu tenho o seguinte exemplo artificial (vindo do código real):

template <class T>
class Base {
public:
 Base(int a):x(a) {}
    Base(Base<T> * &other) { }
    virtual ~Base() {}
private:
 int x;
};

template <class T>
class Derived:public Base<T>{
public:
  Derived(int x):Base<T>(x) {}
  Derived(Derived<T>* &other): Base<T>(other) {}

};


int main() {
 Derived<int> *x=new Derived<int>(1);
 Derived<int> y(x);
}

Quando tento compilar isso, entendo:

1X.cc: In constructor ‘Derived<T>::Derived(Derived<T>*&) [with T = int]’:
1X.cc:27:   instantiated from here
1X.cc:20: error: invalid conversion from ‘Derived<int>*’ to ‘int’
1X.cc:20: error:   initializing argument 1 of ‘Base<T>::Base(int) [with T = int]’

1) Claramente, o GCC está sendo confuso com os construtores. Se eu remover a referência dos construtores, o código compila. Portanto, minha suposição é que algo dá errado com as referências de ponteiro em gaiola. Alguém pode me dizer o que está acontecendo aqui?

2) Uma pergunta um pouco não relacionada. Se eu fizesse algo horrendo como "Excluir outro" no construtor (tenha paciência comigo), o que acontece quando alguém me passa um ponteiro para algo na pilha?

E.g. Derived<int> x(2);
     Derived<int> y(x);

where 

 Derived(Derived<T>*& other) { delete other;}

Como posso garantir que o ponteiro esteja legitimamente apontando para algo na pilha?

Foi útil?

Solução

Base<T> é um tipo base de Derived<T>, mas Base<T>* não é um tipo de base de Derived<T>*. Você pode passar um ponteiro derivado no lugar de um ponteiro base, mas não pode passar por uma referência de ponteiro derivado no lugar de uma referência de ponteiro base.

O motivo é que, suponha que você possa e suponha que o construtor da base fosse escrever algum valor na referência:

Base(Base<T> * &other) {
    Base<T> *thing = new Base<T>(12);
    other = thing;
}

Você acabou de escrever um ponteiro para algo que é não uma Derived<T>, em um ponteiro para Derived<T>. O compilador não pode deixar isso acontecer.

Outras dicas

  1. Você não pode converter uma referência a um ponteiro para derivar para uma referência a um ponteiro para a base. (Os modelos não contribuem para o problema aqui, então removido do meu exemplo abaixo.)
  2. Se você deseja adiar a responsabilidade por um ponteiro, use um tipo de ponteiro inteligente. Os tipos de ponteiro inteligente podem representar a "responsabilidade de excluir" que os ponteiros crus não podem. Exemplos incluem std :: auto_ptr e Boost :: shared_ptr, entre muitos outros.

Por que você não pode aprimorar referências ao ponteiro:

struct Base {};
struct Derived : Base {};
struct Subclass : Base {};

int main() {
  Derived d;
  Derived* p = &d;
  Derived*& d_ptr = p;

  Base*& b_ptr = d_ptr; // this is not allowed, but let's say it is

  Base b;
  b_ptr = &b; // oops! d_ptr no longer points to a Derived!

  Subclass s;
  b_ptr = &s; // oops! d_ptr no longer points to a Derived!
}

Quando você passa seu parâmetro 'outro' para o CTOR base, você está tentando fazer a mesma coisa que b_ptr = d_ptr acima de.

Você garante que o ponteiro aponte para algo na pilha, escrevendo que em sua documentação e confiando no chamador para cumprir isso. Se quem liga para o seu construtor passa por um ponteiro de pilha, todas as apostas estão desligadas e não é sua culpa - você pode tentar pegar o problema mais cedo, mas não há garantias.

É assim que a biblioteca padrão funciona - geralmente captura erros óbvios, mas não é necessário, e cabe ao chamador garantir que eles não estejam fazendo nada estúpido.

Sua x A variável não é um ponteiro, deve ser se você quiser atribuir um new Derived<int> para isso.

Quanto a excluir as coisas na pilha, não faça isso. Não há como saber se você foi aprovado no endereço de algo na pilha ou na pilha (de fato, o padrão C ++ nem reconhece a existência de uma pilha). A lição aqui é que você não deve excluir coisas que não possui, especialmente se não tem como dizer de onde elas vieram.

Não sei por que você deseja referência ao ponteiro. Por que não

Base(Base<T> * other) { }

e

Derived(Derived<T>* other): Base<T>(other) {}

Isso deve funcionar.

E, como outro respondido, acho que você pode legitimamente saber se o ponteiro está apontando para a pilha.

Editar: Por que não se pode fazer o que você está tentando: Considere o exemplo:

Derived1<int> *x = new Derived1<int>
Base<int> **xx =&x;
Derived2<int> y;
*xx = &y;

Onde derivado1 e derivado2 estão diferente classes derivadas da base? Você acha que é legítimo? Agora que x do tipo derivado1* pontos para derivados2?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top