Использование собственного шаблонного класса owner_ptr для выделения многомерного массива
РЕДАКТИРОВАТЬ: я узнал, что мой вопрос содержит парадокс. Я использовал расширенный список инициализаторов в своем коде, который был представлен в C++11, но я хотел использовать только инструменты C++98. Извините, я заметил предупреждение моего компилятора слишком поздно. Конечно, семантика перемещения могла бы решить мою проблему в C++11. Мой вопрос больше не имеет значения.
Я решил создать свой собственный owner_ptr
учебный класс. Концепция заключается в том, что owner_ptr
Объект имеет право собственности на динамически размещенный объект (или объекты). Более 1 owner_ptr
не должен владеть одним и тем же объектом. Вот моя реализация:
#include <cstddef>
template <typename T>
class owner_ptr
{
T* ptr;
bool array;
public:
owner_ptr() : ptr(NULL) {} /* LINE 10 */
owner_ptr(T* ptr, bool isArray = false) : ptr(ptr), array(isArray) {} /* LINE 11 */
owner_ptr(owner_ptr<T>& orig) : ptr(orig.ptr), array(orig.array) /* LINE 12 */
{
orig.ptr = NULL;
}
~owner_ptr()
{
if (ptr != NULL)
{
if (!array)
{
delete ptr;
}
else
{
delete[] ptr;
}
}
}
owner_ptr& operator=(owner_ptr<T>& rvalue)
{
if (this != &rvalue)
{
this->~owner_ptr();
ptr = rvalue.ptr;
array = rvalue.array;
rvalue.ptr = NULL;
}
return *this;
}
void reset()
{
this->~owner_ptr();
ptr = NULL;
}
void addPtr(T* newPtr, bool isArray = false)
{
this->~owner_ptr();
ptr = newPtr;
array = isArray;
}
T& operator*() { return *ptr; }
const T& operator*() const { return *ptr; }
T* get() { return ptr; }
const T* get() const { return ptr; }
T* operator->() { return ptr; }
const T* operator->() const { return ptr; }
T& operator[](int i) { return ptr[i]; }
const T& operator[](int i) const { return ptr[i]; }
};
Я указал, что ответственность за создание юридической ответственности лежит на пользователе owner_ptr
объекты, такие как:
owner_ptr<int> op1(new int);
owner_ptr<int> op2(new int[3], true);
owner_ptr<int> op3(op1);
owner_ptr<int> op4;
op4 = op3;
Хорошо работает с одномерными массивами. Однако, когда я пытаюсь выделить двумерный массив, этот код не компилируется:
int main()
{
owner_ptr< owner_ptr<int> > test(new owner_ptr<int>[2]{ owner_ptr<int>(new int[5], true), owner_ptr<int>(new int[8], true) }, true); /* LINE 72 */
return 0;
}
Я получил следующие сообщения:
\main.cpp|72|error: no matching function for call to 'owner_ptr<int>::owner_ptr(owner_ptr<int>)'|
\main.cpp|72|note: candidates are:|
\main.cpp|12|note: owner_ptr<T>::owner_ptr(owner_ptr<T>&) [with T = int]|
\main.cpp|12|note: no known conversion for argument 1 from 'owner_ptr<int>' to 'owner_ptr<int>&'|
\main.cpp|11|note: owner_ptr<T>::owner_ptr(T*, bool) [with T = int]|
\main.cpp|11|note: no known conversion for argument 1 from 'owner_ptr<int>' to 'int*'|
\main.cpp|10|note: owner_ptr<T>::owner_ptr() [with T = int]|
\main.cpp|10|note: candidate expects 0 arguments, 1 provided|
\main.cpp|72|error: no matching function for call to 'owner_ptr<int>::owner_ptr(owner_ptr<int>)'|
\main.cpp|72|note: candidates are:|
\main.cpp|12|note: owner_ptr<T>::owner_ptr(owner_ptr<T>&) [with T = int]|
\main.cpp|12|note: no known conversion for argument 1 from 'owner_ptr<int>' to 'owner_ptr<int>&'|
\main.cpp|11|note: owner_ptr<T>::owner_ptr(T*, bool) [with T = int]|
\main.cpp|11|note: no known conversion for argument 1 from 'owner_ptr<int>' to 'int*'|
\main.cpp|10|note: owner_ptr<T>::owner_ptr() [with T = int]|
\main.cpp|10|note: candidate expects 0 arguments, 1 provided|
Это так странно. У меня есть следующие вопросы:
- Почему компилятор ищет
owner_ptr<int>::owner_ptr(owner_ptr<int>)
функционировать? Такой конструктор копирования не имеет смысла. - Почему невозможно конвертировать из
'owner_ptr<int>'
в'owner_ptr<int>&'
? - Как я могу это исправить? Можно ли выделить многомерный (используя
owner_ptr
шаблон) только с одной командой?
Я знаю, что это работает:
owner_ptr< owner_ptr<int> > test(new owner_ptr<int>[2], true);
test[0].addPtr(new int[5], true);
test[1].addPtr(new int[8], true);
Однако мне любопытно, возможно ли это сделать одной командой.
ПРИМЕЧАНИЕ: я делаю это в учебных целях. Это не производственный код. Поэтому, пожалуйста, не рекомендуйте мне использовать умные указатели C++11.
2 ответа
Я указал, что ответственность за создание легальных объектов owner_ptr лежит на пользователе:
owner_ptr<int> op1(new int); owner_ptr<int> op2(new int[3], true); owner_ptr<int> op3(op1); owner_ptr<int> op4; op4 = op3;
Это довольно ужасная идея. В идеале ваш owner_ptr
класс должен быть в состоянии вывести это из типа автоматически, если у вас есть T
или T[]
массив.
IIRC Я сделал это для некоторого производственного кода, просто переписав полную специализацию std::auto_ptr<T>
как std::auto_array<T[]>
и сделать это кристально чистым для пользователя.
Это очень похоже на вашу попытку сделать owner_ptr<T>
исправляя право new
а также delete
призывает принять owner_ptr<T[]>
,
С C++98 применяются ограничения (нет type_traits
поддержка), это, кажется, самый простой способ. Вам просто нужно заменить коды, где new
должно быть new[]
а также delete
должно быть delete[]
,
owner_ptr(T* ptr, bool isArray = false) // ^^^^^^^^^^^^^^^^^^^^
Как правило, лучшее и более гибкое решение, чем передача индикатора для типов указателей или массивов, состоит в том, чтобы позволить пользователю предоставлять функцию удаления в конструкторе:
template<typename T>
class owner_ptr {
static void DefaultDeleter(T* p) {
delete p;
}
public:
typedef void(*DeleterFunc)(T*);
static void ArrayDeleter(T* p) {
delete[] p;
}
owner_ptr(T* ptr, DeleterFunc = DefaultDeleter);
};
Что можно потом использовать как
owner_ptr<int> op1(new int());
owner_ptr<int> op2(new int[10],owner_ptr<int>::ArrayDeleter);
и дает больше гибкости в отношении любых пользовательских функций удаления, которые должны вызываться.