Pregunta

Tengo el siguiente ejemplo artificial (procedente de 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);
}

Cuando intento compilar esto, me sale:

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) Es evidente que gcc está siendo confundido por los constructores. Si quito la referencia a partir de los constructores, a continuación, el código se compila. Así que mi suposición es que algo va mal con referencias de puntero-up fundición. ¿Puede alguien decirme lo que está pasando aquí?

2) Una pregunta un poco relacionado. Si tuviera que hacer algo horrendo como "eliminar otros" en el constructor (que tengan paciencia conmigo), lo que sucede cuando alguien me pasa un puntero a algo en la pila?

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

where 

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

¿Cómo puedo estar seguro de que el puntero está apuntando a algo legítimamente en el montón?

¿Fue útil?

Solución

Base<T> es un tipo de base de Derived<T>, pero Base<T>* no es un tipo de base de Derived<T>*. Puede pasar un puntero derivada en lugar de un puntero de base, pero no se puede pasar una referencia de puntero derivada en lugar de una referencia de puntero de base.

La razón es que, supongamos que podía, y supongamos que el constructor de base fueron a escribir algo de valor en la referencia:

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

acabo de escribir un puntero a algo que es no a Derived<T>, en un puntero a Derived<T>. El compilador no puede permitir que esto suceda.

Otros consejos

  1. No se puede convertir una referencia a un puntero a Derivado de una referencia a un puntero a la base. (Plantillas no contribuyen a la cuestión aquí, tan alejado de mi ejemplo a continuación.)
  2. Si desea aplazar la responsabilidad de un puntero, utilice un tipo de puntero inteligente. tipos de punteros inteligentes pueden representar a la "responsabilidad de eliminar" que los punteros primas no pueden. Los ejemplos incluyen std :: auto_ptr y impulsar :: shared_ptr , entre muchos otros.

¿Por qué no se puede upcast referencias de puntero:

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!
}

Al pasar el 'otro' parámetro al ctor Base, que está tratando de hacer lo mismo que b_ptr = d_ptr anteriormente.

Se asegura de que puntero apunta a algo en el montón por escrito que en su documentación y confiar en la persona que llama a cumplir con eso. Si todo el que invoque el constructor pasa un puntero de pila, todas las apuestas están apagadas, y no es su culpa - se puede tratar de coger el problema a tiempo, pero no hay garantías

.

Así es como funciona la biblioteca estándar -. Menudo que va a detectar errores obvios, pero no se requiere para, y le toca a la persona que llama para asegurarse de que no están haciendo nada estúpido

La variable de x no es un puntero, que debería ser si desea asignar un new Derived<int> a él.

En cuanto a la eliminación de las cosas en la pila, no lo hacen. No hay manera de saber si se le ha pasado la dirección de algo en la pila o en el montón (de hecho, el estándar de C ++ ni siquiera reconoce la existencia de una pila). La lección aquí es que no se debe a eliminar las cosas que no le pertenecen, especialmente si usted no tiene forma de saber de dónde venían.

No está seguro de por qué quieres referencia al puntero. ¿Por qué no

Base(Base<T> * other) { }

y

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

Eso debería funcionar.

Y, al igual que otros respondieron: No creo que se puede saber legítimamente si el puntero está apuntando en montón.

Editar : ¿por qué no se puede hacer lo que usted está tratando de: considere ejemplo:

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

Cuando Derived1 y se Derived2 clases derivadas de la base? Pensarías que es legítimo? Ahora que x del tipo Derived1 puntos * hacia Derived2?

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top