Уникальный указатель и правильность const
Я не ожидал, что этот код скомпилируется:
#include <iostream>
#include <memory>
class A
{
public:
inline int get() const
{
return m_i;
}
inline void set(const int & i)
{
m_i = i;
}
private:
int m_i;
};
int main()
{
const auto ptr = std::make_unique< A >();
ptr->set( 666 ); // I do not like this line D:<
std::cout << ptr->get( ) << std::endl;
return 0;
}
Если бы ptr был необработанным указателем C, я был бы согласен с этим. Но так как я использую умный указатель, я не могу понять, в чем причина этого.
Я использую уникальный указатель для выражения владения, в объектно-ориентированном программировании это можно рассматривать как композицию объекта (отношение "часть").
Например:
class Car
{
/** Engine built through some creational OO Pattern,
therefore it has to be a pointer-accessed heap allocated object **/
std::unique_ptr< Engine > m_engine;
};
Или же:
class A
{
class Impl;
std::unique_ptr< A::Impl > m_impl; // PIMPL idiom
};
Если экземпляр класса Car является постоянным, то почему Engine также не должен быть постоянным? Если бы это был общий указатель, я бы полностью согласился с этим.
Есть ли умный указатель, который может отражать поведение, которое я хочу?
2 ответа
Это довольно просто:
const auto ptr = std::make_unique< A >();
Это означает, что сам указатель является постоянным! Но объект, который он держит, - нет. Вы можете видеть, что это работает наоборот...
A *const ptr = new A();
Это то же самое. Указатель является постоянным (не может быть изменен, чтобы указывать в другом месте), но объект не является.
Теперь вы, вероятно, имели в виду, что хотите что-то подобное, нет?
const auto ptr = std::make_unique<const A>();
Это создаст постоянный указатель на постоянную A
,
Есть и другой способ...
auto ptr = std::make_unique<const A>();
Объект является константой, но не указателем.
Кстати: это "распространение констант", о котором вы говорите, применимо и к C++, так же, как вы это заявили.
Хотя это поздний ответ, есть решение для распространения константности в классе.
std::experimental::propagate_const :
std::experimental::propagate_const — это оболочка, распространяющая const для указателей и объектов, подобных указателям. Он обрабатывает обернутый указатель как указатель на const при доступе через путь доступа const, отсюда и название.
#include <iostream>
#include <memory>
#include <experimental/propagate_const>
class Car
{
public:
void start();
void start() const;
private:
struct Engine;
std::experimental::propagate_const<std::unique_ptr<Engine>> m_engine;
};
struct Car::Engine{
void start(){
std::cout<< "Engine started (non const)"<<std::endl;
}
void start() const{
std::cout<<"Engine started (const)" <<std::endl;
}
};
void Car::start(){
m_engine->start();
std::cout<< "Car started (non const)"<<std::endl;
}
void Car::start() const{
m_engine->start();
std::cout<< "Car started (const)"<<std::endl;
}
int main()
{
Car c1;
c1.start();
const Car c2;
c2.start();
return 0;
}
п/п:
Engine started (non const)
Car started (non const)
Engine started (const)
Car started (const)