C++ getters/setters编码风格

Ale*_*lex 65 c++ coding-style getter-setter

我已经在C#中编程了一段时间,现在我想要提高我的C++技能.

上课:

class Foo
{
    const std::string& name_;
    ...
};
Run Code Online (Sandbox Code Playgroud)

什么是最好的方法(我只想允许对name_字段的读访问):

  • 使用getter方法: inline const std::string& name() const { return name_; }
  • 让这个领域公开,因为它是一个常数

谢谢.

j_r*_*ker 78

对于长寿命类,使用getter方法是一种更好的设计选择,因为它允许您将getter方法替换为将来更复杂的东西.虽然这似乎不太可能需要const值,但成本很低,可能的好处很大.

顺便说一下,在C++中,为成员提供相同名称的getter和setter是一个特别好的主意,因为将来你可以实际更改这对方法:

class Foo {
public:
    std::string const& name() const;          // Getter
    void name(std::string const& newName);    // Setter
    ...
};
Run Code Online (Sandbox Code Playgroud)

成为一个公共成员变量,operator()()为每个变量定义:

// This class encapsulates a fancier type of name
class fancy_name {
public:
    // Getter
    std::string const& operator()() const {
        return _compute_fancy_name();    // Does some internal work
    }

    // Setter
    void operator()(std::string const& newName) {
        _set_fancy_name(newName);        // Does some internal work
    }
    ...
};

class Foo {
public:
    fancy_name name;
    ...
};
Run Code Online (Sandbox Code Playgroud)

当然需要重新编译客户端代码,但不需要更改语法!显然,这种转换对于const值也同样适用,其中只需要一个getter.

  • 嘿,我在发布我的问题之前找到了你的答案......它似乎几乎完全一样,看起来你提出了我正在寻找的解决方案类型但是我不能确定它是一个菜鸟.你介意看看我的问题:http://stackoverflow.com/questions/8454887/creating-read-only-public-class-members-in-c/8454913#8454913并让我知道这个答案是否是在我想要完成的事情的正确轨道上? (2认同)

Mr *_*ooz 46

将非const字段公开是一个坏主意,因为很难强制错误检查约束和/或在将来为值变化添加副作用.

在您的情况下,您有一个const字段,因此上述问题不是问题.使其成为公共领域的主要缺点是您要锁定底层实现.例如,如果将来您想要将内部表示更改为C字符串或Unicode字符串或其他内容,那么您将中断所有客户端代码.使用gettor,您可以转换为现有客户端的旧表示,同时通过新的gettor为新用户提供更新的功能.

我仍然建议你使用上面提到的getter方法.这将最大化您未来的灵活性.

  • ..如果您的内部表示是Unicode,但是您可以转换为UTF8以与现有客户端兼容?getter方法可以进行转换,但公共字段会禁止此模式. (6认同)

chr*_*ish 21

顺便说一句,在C++中,拥有一个const引用成员有点奇怪.您必须在构造函数列表中分配它.谁拥有该对象的实际记忆以及它的生命周期是什么?

至于风格,我同意其他人你不想暴露你的私人.:-)我喜欢这种模式的setter/getters

class Foo
{
public:
  const string& FirstName() const;
  Foo& FirstName(const string& newFirstName);

  const string& LastName() const;
  Foo& LastName(const string& newLastName);

  const string& Title() const;
  Foo& Title(const string& newTitle);
};
Run Code Online (Sandbox Code Playgroud)

这样你可以做类似的事情:

Foo f;
f.FirstName("Jim").LastName("Bob").Title("Programmer");
Run Code Online (Sandbox Code Playgroud)

  • 虽然它的读写能力很好,但方法链接('流畅')API风格带来了一些开销:http://stackoverflow.com/q/3134416/102345 (3认同)

Lam*_*age 8

我认为C++ 11的方法现在更像是这样.

#include <string>
#include <iostream>
#include <functional>

template<typename T>
class LambdaSetter {
public:
    LambdaSetter() :
        getter([&]() -> T { return m_value; }),
        setter([&](T value) { m_value = value; }),
        m_value()
    {}

    T operator()() { return getter(); }
    void operator()(T value) { setter(value); }

    LambdaSetter operator=(T rhs)
    {
        setter(rhs);
        return *this;
    }

    T operator=(LambdaSetter rhs)
    {
        return rhs.getter();
    }

    operator T()
    { 
        return getter();
    }


    void SetGetter(std::function<T()> func) { getter = func; }
    void SetSetter(std::function<void(T)> func) { setter = func; }

    T& GetRawData() { return m_value; }

private:
    T m_value;
    std::function<const T()> getter;
    std::function<void(T)> setter;

    template <typename TT>
    friend std::ostream & operator<<(std::ostream &os, const LambdaSetter<TT>& p);

    template <typename TT>
    friend std::istream & operator>>(std::istream &is, const LambdaSetter<TT>& p);
};

template <typename T>
std::ostream & operator<<(std::ostream &os, const LambdaSetter<T>& p)
{
    os << p.getter();
    return os;
}

template <typename TT>
std::istream & operator>>(std::istream &is, const LambdaSetter<TT>& p)
{
    TT value;
    is >> value;
    p.setter(value);
    return is;
}


class foo {
public:
    foo()
    {
        myString.SetGetter([&]() -> std::string { 
            myString.GetRawData() = "Hello";
            return myString.GetRawData();
        });
        myString2.SetSetter([&](std::string value) -> void { 
            myString2.GetRawData() = (value + "!"); 
        });
    }


    LambdaSetter<std::string> myString;
    LambdaSetter<std::string> myString2;
};

int _tmain(int argc, _TCHAR* argv[])
{
    foo f;
    std::string hi = f.myString;

    f.myString2 = "world";

    std::cout << hi << " " << f.myString2 << std::endl;

    std::cin >> f.myString2;

    std::cout << hi << " " << f.myString2 << std::endl;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我在Visual Studio 2013中对此进行了测试.不幸的是,为了使用LambdaSetter中的底层存储,我需要提供一个"GetRawData"公共访问器,这可能导致破坏的封装,但是你可以将其保留并提供自己的存储容器或者只是确保您在编写自定义getter/setter方法时使用"GetRawData"的唯一时间.

  • 这不是一个矫枉过正吗?这种方法使用了很好的封装,所以你只需要维护``LambdaSetter``类,但从性能的角度来看,我认为摆脱函数指针会是一个好主意吗?而且``myString.SetGetter(``看起来有点尴尬,给对象一个能够执行给定动作的函数,即设置``myString``.这种方法肯定是朝着正确的方向发展的但是我会清楚地简化这个,而没有``std :: functions`` (2认同)
  • 我将其称为过早优化。 (2认同)

Dan*_*lau 5

即使名称是不可变的,您仍然可能希望选择计算它而不是将其存储在字段中.(我意识到这不太可能是"名字",但我们的目标是一般情况.)因此,即使是常数字段也最好包含在getter中:

class Foo {
    public:
        const std::string& getName() const {return name_;}
    private:
        const std::string& name_;
};
Run Code Online (Sandbox Code Playgroud)

请注意,如果要更改getName()为返回计算值,则无法返回const ref.没关系,因为它不需要对调用者进行任何更改(模数重新编译).