Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/jsf-2/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何编写C++;接球手和接球手_C++_Getter Setter - Fatal编程技术网

C++ 如何编写C++;接球手和接球手

C++ 如何编写C++;接球手和接球手,c++,getter-setter,C++,Getter Setter,如果我需要为属性编写setter和/或getter,我可以这样编写: struct X { /*...*/}; class Foo { private: X x_; public: void set_x(X value) { x_ = value; } X get_x() { return x_; } }; template <class T> class checked { T v

如果我需要为属性编写setter和/或getter,我可以这样编写:

struct X { /*...*/};

class Foo
{
private:
    X x_;

public:
    void set_x(X value)
    {
        x_ = value;
    }
    X get_x()
    {
        return x_;
    }
};
template <class T>
class checked {
    T value;
    std::function<T(T const &)> check;

public:
    template <class checker>
    checked(checker check) 
        : check(check)
        , value(check(T())) 
    { }

    checked &operator=(T const &in) { value = check(in); return *this; }

    operator T() const { return value; }

    friend std::ostream &operator<<(std::ostream &os, checked const &c) {
        return os << c.value;
    }

    friend std::istream &operator>>(std::istream &is, checked &c) {
        try {
            T input;
            is >> input;
            c = input;
        }
        catch (...) {
            is.setstate(std::ios::failbit);
        }
        return is;
    }
};
<>但是我听说这是java写的设置和吸收器的样式,我应该用C++风格来写。此外,有人告诉我这是无效的,甚至是不正确的。这是什么意思?我如何编写C++中的设置器和吸气剂?< /P>

假设对getter和/或setter的需求是合理的。例如,可能我们在setter中执行一些检查,或者可能我们只编写getter。

这就是我编写通用setter/getter的方式:

class Foo
{
private:
    X x_;

public:
    X&       x()        { return x_; }
    const X& x() const  { return x_; }
};
我将尝试解释每个转换背后的原因:

您的版本的第一个问题是,您应该传递常量引用,而不是传递值。这避免了不必要的复制。是的,因为
C++11
可以移动该值,但这并不总是可能的。对于基本数据类型(例如,
int
),使用值而不是引用是可以的

所以我们先纠正一下

class Foo1
{
private:
    X x_;

public:
    void set_x(const X& value)
//             ^~~~~  ^
    {
        x_ = value;
    }

    const X& get_x()
//  ^~~~~  ^
    {
        return x_;
    }
};
仍然上述解决方案存在问题。由于
get_x
不修改对象,因此应将其标记为
const
。这是C++原理的一部分,称为const正确性。

上述解决方案不允许您从
const
对象获取属性:

const Foo1 f;

X x = f.get_x(); // Compiler error, but it should be possible
这是因为不能对const对象调用不是const方法的
get_x
。这样做的理由是非常量方法可以修改对象,因此在常量对象上调用它是非法的

因此,我们进行了必要的调整:

class Foo2
{
private:
    X x_;

public:
    void set_x(const X& value)
    {
        x_ = value;
    }

    const X& get_x() const
//                   ^~~~~
    {
        return x_;
    }
};
上述变体是正确的。但是在C++中,还有一种写C++的方法,它是C++ + ISH,更少的java ISH。 有两件事需要考虑:

  • 我们可以返回对数据成员的引用,如果我们修改了该引用,我们实际上会修改数据成员本身。我们可以用它来编写setter
  • <> LI>在C++方法中可以单独地重载。
因此,通过以上的知识,我们可以写出我们最后一个优雅的C++版本:

最终版本 作为个人偏好,我使用新的尾部返回函数样式。(例如,代替
int foo()
I编写
auto foo()->int

class Foo
{
private:
    X x_;

public:
    auto x()       -> X&       { return x_; }
    auto x() const -> const X& { return x_; }
};
现在我们将调用语法更改为:

Foo2 f;
X x1;

f.set_x(x1);
X x2 = f.get_x();
致:

超越最终版本 出于性能原因,我们可以更进一步,在
&&
上重载,并返回对
x的右值引用,从而允许在需要时从它移动

class Foo
{
private:
    X x_;

public:
    auto x() const& -> const X& { return x_; }
    auto x() &      -> X&       { return x_; }
    auto x() &&     -> X&&      { return std::move(x_); }

};


非常感谢在评论中收到的反馈,特别是感谢StorryTeller对改进此帖子提出的宝贵建议。

您的主要错误是,如果在API参数和返回值中不使用引用,那么您可能会在get/set操作(“may”)中执行不必要的拷贝因为如果您使用优化器,您的编译器可能会避免这些副本)

我会这样写:

class Foo
{
private:
    X x_;
public:
    void x(const X &value) { x_ = value; }
    const X &x() const { return x_; }
};

这将保持<强> const正确< <强> >,这是C++的一个非常重要的特性,它与旧的C++版本兼容(另一个答案需要C++ 11)。 您可以将该类用于:

Foo f;
X obj;
f.x(obj);
X objcopy = f.x(); // get a copy of f::x_
const X &objref = f.x(); // get a reference to f::x_
我发现在u或camel case(即getX(),setX())中使用get/set都是多余的,如果您做错了什么,编译器将帮助您解决问题

如果要修改内部Foo::X对象,还可以添加第三个重载X():

…通过这种方式,您可以编写如下内容:

Foo f;
X obj;
f.x() = obj; // replace inner object
f.x().int_member = 1; // replace a single value inside f::x_

但我建议您避免这种情况,除非您确实需要经常修改内部结构(X)。

标准库中出现了两种不同形式的“属性”,我将其归类为“面向身份”和“面向价值”。选择哪一个取决于系统应如何与
Foo
交互。两者都不是“更正确”

身份导向

class Foo
{
     X x_;
public:
          X & x()       { return x_; }
    const X & x() const { return x_; }
}
这里我们返回一个对底层
X
成员的引用,它允许呼叫站点的双方观察对方发起的更改。外部世界可以看到
X
成员,可能是因为它的身份很重要。乍一看,它可能看起来只有“get”属性的一侧,但如果
X
是可赋值的,则情况并非如此

 Foo f;
 f.x() = X { ... };
价值导向

class Foo
{
     X x_;
public:
     X x() const { return x_; }
     void x(X x) { x_ = std::move(x); }
}

在这里,我们返回
X
成员的一个副本,并接受一个副本来覆盖。以后任何一方的更改都不会传播。在这种情况下,我们大概只关心
X
的值。

多年来,我逐渐相信getter/setter的整个概念通常是一个错误。听起来可能相反,public变量通常是正确答案

诀窍在于公共变量应该是正确的类型。在问题中,您已经指定要么我们编写了一个setter来检查正在编写的值,要么我们只编写了一个getter(因此我们有一个有效的
const
对象)

我想说这两个词基本上都是这样说的:“X是一个int,只是它不是一个int-,它确实有点像int,但是有这些额外的限制……”

这就引出了真正的问题:如果仔细观察X表明它确实是一种不同的类型,那么定义它真正的类型,然后将其创建为该类型的公共成员。它的基本结构可能如下所示:

struct X { /*...*/};

class Foo
{
private:
    X x_;

public:
    void set_x(X value)
    {
        x_ = value;
    }
    X get_x()
    {
        return x_;
    }
};
template <class T>
class checked {
    T value;
    std::function<T(T const &)> check;

public:
    template <class checker>
    checked(checker check) 
        : check(check)
        , value(check(T())) 
    { }

    checked &operator=(T const &in) { value = check(in); return *this; }

    operator T() const { return value; }

    friend std::ostream &operator<<(std::ostream &os, checked const &c) {
        return os << c.value;
    }

    friend std::istream &operator>>(std::istream &is, checked &c) {
        try {
            T input;
            is >> input;
            c = input;
        }
        catch (...) {
            is.setstate(std::ios::failbit);
        }
        return is;
    }
};
这样,我们就可以安全地将成员公开,因为我们定义的类型实际上就是我们想要的类型——我们想要在其上设置的条件是该类型固有的,而不是由getter/setter在事实之后附加的东西

当然,在这种情况下,我们需要以某种方式限制值。如果我们只需要一个有效的只读类型,那就容易多了——只需要一个定义构造函数和
操作符T的模板<
class Foo
{
     X x_;
public:
     X x() const { return x_; }
     void x(X x) { x_ = std::move(x); }
}
template <class T>
class checked {
    T value;
    std::function<T(T const &)> check;

public:
    template <class checker>
    checked(checker check) 
        : check(check)
        , value(check(T())) 
    { }

    checked &operator=(T const &in) { value = check(in); return *this; }

    operator T() const { return value; }

    friend std::ostream &operator<<(std::ostream &os, checked const &c) {
        return os << c.value;
    }

    friend std::istream &operator>>(std::istream &is, checked &c) {
        try {
            T input;
            is >> input;
            c = input;
        }
        catch (...) {
            is.setstate(std::ios::failbit);
        }
        return is;
    }
};
checked<int> foo([](auto i) { return std::min(std::max(i, 0), 10); });
std::cout << "Please enter a number from 0 to 10: ";
std::cin >> foo; // inputs will be clamped to range

std::cout << "You might have entered: " << foo << "\n";

foo = foo - 20; // result will be clamped to range
std::cout << "After subtracting 20: " << foo;