Уникальный указатель и правильность 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)

ДЕМО

Другие вопросы по тегам