Указатель восходящего указателя - PullRequest
3 голосов
/ 23 декабря 2009

У меня есть следующий надуманный пример (из реального кода):

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

Когда я пытаюсь скомпилировать это, я получаю:

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) Очевидно, что gcc сбит с толку конструкторами. Если я удалю ссылку из конструкторов, то код компилируется. Поэтому я предполагаю, что что-то идет не так с повышением ссылки указателя. Может кто-нибудь сказать мне, что здесь происходит?

2) Слегка не связанный вопрос. Если бы я должен был сделать что-то ужасное, как «удалить других» в конструкторе (потерпи меня), что происходит, когда кто-то передает мне указатель на что-то в стеке?

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

where 

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

Как я могу убедиться, что указатель законно указывает на что-то в куче?

Ответы [ 5 ]

10 голосов
/ 23 декабря 2009

Base<T> является базовым типом Derived<T>, но Base<T>* не является базовым типом Derived<T>*. Вы можете передать производный указатель вместо базового указателя, но вы не можете передать ссылку на производный указатель вместо ссылки базового указателя.

Причина в том, что, предположим, вы могли бы, и предположим, что конструктор Base должен был записать некоторое значение в ссылку:

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

Вы только что написали указатель на что-то, что не a Derived<T>, в указатель на Derived<T>. Компилятор не может этого допустить.

3 голосов
/ 23 декабря 2009
  1. Невозможно преобразовать ссылку на указатель на производное в ссылку на указатель на базу. (Шаблоны здесь не помогают, поэтому удалите из моего примера ниже.)
  2. Если вы хотите отложить ответственность за указатель, используйте умный тип указателя. Интеллектуальные типы указателей могут представлять «ответственность за удаление», чего не могут необработанные указатели. Примеры включают std :: auto_ptr и boost :: shared_ptr , среди многих других.

Почему вы не можете ссылаться на указатель вверх:

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

Когда вы передаете свой «другой» параметр в базовый ctor, вы пытаетесь сделать то же самое, что и b_ptr = d_ptr выше.

2 голосов
/ 23 декабря 2009

Вы убедитесь, что указатель указывает на что-то в куче, записав это в своей документации и полагаясь на собеседника, соблюдающего это. Если тот, кто вызывает ваш конструктор, передает указатель стека, все ставки отключены, и это не ваша ошибка - вы можете попытаться поймать проблему раньше, но без гарантий.

Так работает стандартная библиотека - часто она будет отлавливать очевидные ошибки, но это не обязательно, и вызывающий должен убедиться, что они не делают глупостей.

0 голосов
/ 23 декабря 2009

Не уверен, зачем вам ссылка на указатель. Почему не

Base(Base<T> * other) { }

и

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

Это должно сработать.

И, как другие ответили, я не думаю, что вы можете на законных основаниях знать, указывает ли указатель в кучу.

Редактировать : почему нельзя делать то, что ты пытаешься: рассмотрим пример:

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

Где Derived1 и Derived2 - это разные классы, полученные из Base? Вы думаете, что это законно? Теперь, когда x типа Derived1 * указывает на Derived2?

0 голосов
/ 23 декабря 2009

Ваша переменная x не является указателем, она должна быть, если вы хотите присвоить ей new Derived<int>.

Что касается удаления вещей в стеке, не делайте этого. Невозможно определить, передан ли вам адрес чего-либо в стеке или в куче (действительно, стандарт C ++ даже не признает существование стека). Урок здесь заключается в том, что вы не должны удалять вещи, которые вам не принадлежат, особенно если вы не можете сказать, откуда они пришли.

...