Я перехожу с указателей C на C ++, теперь изучаю auto_ptr. Вот программа, которую я попробовал:
#include <iostream>
#include <memory>
#include "Car.h"using namespace std;
typedef auto_ptr<Car> CarPtr;
int main() {
CarPtr au_ptr1(new Car());
CarPtr au_ptr2 = new Car();
Car *norm_ptr1 = new Car();
Car *norm_ptr2(new Car());
int *i_ptr1=new int();
int *i_ptr2(new int());
}
Утверждения вроде следующего означают что?
int *i_ptr2(new int());
Car *norm_ptr2(new Car());
Упомянутое выше утверждение успешно скомпилировано. И следующее выдает ошибку компиляции: CarPtr au_ptr2 = new Car();
Это почему?
Заранее спасибо
У него есть конструктор копирования, но конструктор преобразования является явным, что является причиной ошибки:
explicit auto_ptr (X* p=0) throw();
Что означает Car*
не может быть неявно преобразовано в auto_ptr<Car>
, который является то, что
CarPtr au_ptr2 = new Car();
попытки сделать. Это называется копия инициализация, в отличие от:
CarPtr au_ptr1 (new Car());
который Значение инициализация. Первая версия попытается создать временную CarPtr
из Car*
и использовать его для инициализации au_ptr2
, Второй вызывает конструктор копирования напрямую.
Заявления как
int *i_ptr2(new int());
просто инициализируйте значение указателя со значением в скобках.
Обычно объекты имеют конструктор копирования, а указатели не являются объектами, поэтому у них нет конструктора копирования (или оператора присваивания, или деструктора). Точнее, указатели полагаются на механизм копирования по умолчанию.
Когда вы говорите о auto_ptr
или любые другие умные указатели, они просто однофамильцы указателей. Но на самом деле они Templatized объекты которые используют механизм RAII.
CarPtr au_ptr2 = new Car(); // this is initialization not assignment
дает ошибку компиляции, потому что соответствующий CarPtr::CarPtr(...)
конструктор сделан explicit
поэтому он не принимает =
инициализация стиля.
Необработанные указатели не имеют конструкторов, но для большинства целей их можно использовать так, как если бы они были. Все встроенные типы могут быть инициализированы из значения любого типа, преобразуемого в их тип, точно так же, как это может сделать пользовательский класс с конструктором копирования.
int *i_ptr1=new int();
int *i_ptr2(new int());
значит то же самое.
Я думаю, что причина этого в основном шаблоны: это означает, что вы можете использовать тип T
как будто это был пользовательский тип, и напишите T t(0);
или же T(0)
или же T()
, и когда T
бывает встроенным типом значение точно такое же, как T t = 0;
или же (T)0
или же (T)0
(снова). На самом деле, значение T(0)
является по определению такой же как (T)0
независимо от того, какие конструкторы T
имеет, но люди, которые говорят вам не использовать приведение в стиле C в коде C ++, пытаются игнорировать этот факт 😉
auto_ptr
на самом деле имеет конструктор копирования, но, в отличие от большинства копий, он принимает неконстантный параметр и изменяет свой аргумент. Вот почему в C ++ 11 это устарело в пользу unique_ptr
, который не имеет конструктора копирования, но имеет конструктор перемещения.
Как говорит Лучиан, проблема с CarPtr au_ptr2 = new Car();
это не (просто) конструктор копирования, это также отсутствие неявного преобразования из типа new Car();
, Car*
, чтобы auto_ptr<Car>
, Инициализация копирования пытается неявно преобразовать RHS в тип LHS, а затем скопировать его в LHS. Оба из них терпят неудачу в этом примере. Прямая инициализация допускает использование явных преобразований и не требует копирования, поэтому она успешно выполняется.
Одним из способов, которым встроенные типы не ведут себя так, как будто они имеют конструкторы, является инициализация по умолчанию. Ты можешь написать:
int i = int();
а также i
гарантированно инициализируется до нуля. Таким образом, вы можете представить, что у него есть конструктор без аргументов, который устанавливает его на ноль. Но если int
были действительно тип класса с этим конструктором, а затем писать:
int i;
также гарантирует i
ноль, и это не так (по крайней мере, не в области действия функции).
Кстати, не слишком волнуйтесь обо всем этом и случайно вызывайте так называемый «самый неприятный анализ».
int i();
эквивалентно int i(void);
не int i(0);
, Он объявляет функцию, а не целочисленную переменную.