Справочник по указателю с установкой
-
21-09-2019 - |
Вопрос
У меня есть следующий надувший пример (исходя из реального кода):
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;}
Как я могу убедиться, что этот указатель законно указывает на что -то на кучу?
Решение
Base<T>
это базовый тип Derived<T>
, но Base<T>*
не является базовым типом Derived<T>*
. Анкет Вы можете передать полученный указатель вместо базового указателя, но вы не можете передать полученную ссылку на указатель вместо базовой ссылки.
Причина в том, что, предположим, вы могли бы, и предположим, что конструктор базы должен был написать некоторое значение в ссылку:
Base(Base<T> * &other) {
Base<T> *thing = new Base<T>(12);
other = thing;
}
Вы только что написали указатель на что -то, что нет а Derived<T>
, в указатель на Derived<T>
. Анкет Компилятор не может позволить этому случиться.
Другие советы
- Вы не можете преобразовать ссылку на указатель на получение ссылки на указатель на основание. (Шаблоны не способствуют проблеме здесь, поэтому удалены из моего примера ниже.)
- Если вы хотите отложить ответственность за указатель, используйте тип интеллектуального указателя. Умные типы указателей могут представлять «ответственность за удаление», которую не могут сырые указатели. Примеры включают 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
выше.
Вы убедитесь, что указатель указывает на что -то на кучу, написав это в своей документации и полагаясь на абонента, чтобы соблюдать это. Если тот, кто называет ваш конструктор, передает указатель стека, все ставки выключены, и это не ваша вина - вы можете попытаться уловить проблему рано, но никаких гарантий.
Вот как работает стандартная библиотека - часто она поймает очевидные ошибки, но это не обязательно, и вызывник должен убедиться, что они не делают ничего глупого.
Ваш x
переменная не является указателем, это должно быть, если вы хотите назначить new Derived<int>
к этому.
Что касается удаления вещей в стеке, не делайте этого. Невозможно сказать, были ли вы передали адрес чего -то в стеке или в куче (действительно, стандарт C ++ даже не признает существование стека). Урок здесь заключается в том, что вы не должны удалять вещи, которые у вас нет, особенно если у вас нет возможности сказать, откуда они пришли.
Не уверен, зачем вам ссылку на указатель. Почему бы и нет
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;
Где получено1 и получено2 другой классы, полученные из базы? Вы бы подумали, что это законно? Теперь, что x типа, полученного1* указывает на полученное2?