2015-10-21 4 views
1

Является ли неправильной практикой отмечать метод как const, хотя возвращаемый им элемент данных (указателя) может быть использован «вне» метода для изменения его ссылочного объекта?Функция константного элемента C++ возвращает указатель

Посмотрите на метод bone().

class SceneNode 
{ 
public: 
    SceneNode(const char *nodeName, SceneNode *parent); 
    ~SceneNode(); 

    void setBone(Bone *bone); 
    Bone* bone() const { return _bone; } 
}; 

private: 
    Bone *_bone; 
}; 

Я спрашиваю это потому, что это кажется немного странным для меня, как метод не может быть константной, если _bone не указатель, а объект (если он не возвращает константную ссылку или копию, из курс).

+0

Это зависит от вашего варианта использования, но в этом случае имеет смысл возвращать 'const Bone *'. –

+0

Это определенно рецепт катастрофы. – user657267

+0

Я хочу иметь возможность изменить ссылочный объект _bone. Я спрашиваю, если это плохая практика, чтобы установить метод как const – Pilpel

ответ

4

Это зависит Например, unique_ptr::operator*() помечается как константы, но не добавляет дополнительный сопзЬ к опорной она возвращается. Это имеет смысл, поскольку аналогично возможности изменять то, на что указывает T *const, но не изменять сам указатель.

const std::unique_ptr<int> uptr(new int); // make_unique is better 
*uptr = 1; // sure, I can modify what it points to 
uptr.reset(); // no, this modifies the pointer itself (conceptually) 

С другой стороны, что-то вроде std::string имея char& operator[](size_t) const; было бы странно, потому что char s внутри него должен действовать как часть объекта они ссылаются (в string классе)

const std::string s{"hello world"}; // I expect this to always be "hello world" 
s[0] = 'a'; // error, you're conceptually modifying the string 

Вам решать, что имеет смысл в вашем API и когда ваш класс «морально const». Если у вас

void fun(const SceneNode& node); 
int main() { 
    SceneNode n(...); 
    Bone b; 
    n.setBone(&b); 
    fun(n); 
    assert(*n.bone() == b); // is this a safe assumption? 
} 

Если вы считаете, что утверждение всегда следует держать, а затем возвращать const Bone*.

+0

Чтобы «решить» вашу последнюю проблему с кодом, я мог бы просто добавить второй метод? 'const Bone * bone() const {return _bone; } '. Это будет гарантировать, что 'node' в fun всегда будет вызывать версию const? (и поэтому изменение на 'b' никогда не произойдет) – Pilpel

+0

@Pilpel right. это обычный способ, так же, как у вас есть 'T & operator [] (size_t);' и 'const T & operator [] (size_t) const' в контейнерах, таких как вектор –

2

Установка const по результатам методы в хорошо определенной поведения, но он все еще может быть заблуждением поведения. Независимо от того, запутывает ли он или нет, зависит от того, что должен представлять класс и какую ментальную модель должен иметь программист.

Для аналогичного примера рассмотрите std::vector. Этот класс также имеет элемент данных, который является указателем, и у него есть метод для извлечения этого указателя; например что-то вроде

template< typename T > 
class vector 
{ 
    T *elements; 
public: 
    T* data() { return elements; } 
}; 

Что относительно версии const? Ну, то, что экземпляр этого класса представляет, будет изменен, если вы измените то, на что ссылается указатель; т. е. если вы измените элемент вектора, то вы изменили вектор. Следовательно, константная версия не должен возвращать указатель на неконстантный:

template< typename T > 
class vector 
{ 
    T *elements; 
public: 
    T* data() { return elements; } 
    const T* data() const { return elements; } 
}; 

С другой стороны, рассмотрит std::shared_ptr также имеет элемент данных, который является указателем, и есть метод для получения этого указателя:

template< typename T > 
class shared_ptr 
{ 
    T *ptr; 
public: 
    T* get() { return ptr; } 
}; 

Что делать, если мы получим указатель и изменим то, на что оно указывает? Ну, мы совсем не изменили shared_ptr: это еще, указывая на этот объект. Поэтому в этом случае мы должны иметь

template< typename T > 
class shared_ptr 
{ 
    T *ptr; 
public: 
    T* get() const { return ptr; } 
}; 
Смежные вопросы