当有私有变量时,为什么我需要使引用保持不变?

时间:2013-10-08 20:02:25

标签: c++ c++11 operator-overloading

这是http://www.learncpp.com/cpp-tutorial/92-overloading-the-arithmetic-operators/

的一些代码

我一直在调查运营商从几个自学网站和浏览论坛中超载。我问了一个关于使用不同方法重载的问题,我现在明白了。该Q& A链接在How is this returning a value when it has not been defined?

但是,这个方法带参考我不明白。我试图用不同的方式来实现代码,并且已经看到一些不那么复杂的工作方式。

我理解这样做的方法是将类的实例作为操作符函数的参数,操作符函数中的临时实例,并使用=运算符返回这些值。这种方式要复杂得多,我有几个问题就是为什么这种方法有效。

问题在于您下载代码时的措辞,但以下是我提出的三个问题 (问题1)Ceteris parabis,为什么我需要参数中的const关键字?我知道,如果我将变量公开,我不会,但为什么如果有一个朋友类,或者如果代码是在类本身内写的,我是否需要使用const。 (问题2)如果我把朋友功能放在课堂里,我还需要关键词“朋友”为什么?
(问题3)c1和c2类在哪里初始化?有一个对它们的引用,但是在返回之前没有初始化,但是它低于引用。我认为编译时会出错。

class Cents
{
private:
    int m_nCents;

public:
    Cents(int nCents) { m_nCents = nCents; }
    //I know this assigns each
    //instance to the private variable m_nCents since it's private.

    // Add Cents + Cents 
    friend Cents operator+(const Cents &c1, const Cents &c2); 

    //why do we need
    //to make this a friend? why can't we just put it inside the class and not  
    //use the "friend" keyword? also why do I need to make the variables public
    //if i remove const from the parameters

    int GetCents() { return m_nCents; } 
    //I know how this is used to return the
    // variable stored in m_nCents, in this program it is for cout
};

// note: this function is not a member function!
Cents operator+(const Cents &c1, const Cents &c2)
//where are these references 
//actually defined? I do not see c1 or c2 anywhere except in the return, but
//that is below the code that is referencing the class
{
    // use the Cents constructor and operator+(int, int)
    return Cents(c1.m_nCents + c2.m_nCents);
}

int main()
{
    Cents cCents1(6);
    Cents cCents2(8);
    Cents cCentsSum = cCents1 + cCents2;
    std::cout << "I have " << cCentsSum .GetCents() << " cents." << std::endl;

    return 0;
}

2 个答案:

答案 0 :(得分:4)

以下可能更具说明性:当您计算总和时,您的代码相当于:

Cents cCentsSum = operator+(cCents1,cCents2);

(是的,以上是实际的法律代码)。

这更类似于函数调用,实际上是运算符。引用是参数,因此const Cents& c1引用cCents1const Cents& c2引用cCents2

现在需要实现此免费功能,但您正在访问c1.m_cents private。为了实现这一点,您需要将方法声明为friend,否则您编写的自由函数将产生访问错误。

仍然需要将它声明为friend,即使你把它放在了类中,因为运算符是特殊的。编译器看到你的operator +有两个参数,因此它知道这是而不是一个成员函数。它实际上等同于自由函数的代码,只是更紧凑。

最后,您希望这些引用为const以避免意外修改。考虑一下你是否写道:

Cents operator+(Cents &c1, Cents &c2)
{
    return Cents(c1.m_nCents += c2.m_nCents);
}

请注意+=而不是+,它会修改c1的值。在您的示例中,这意味着之后调用

Cents cCentsSum = cCents1+cCents2;

cCents1的值将被修改。当然不可取。通过使参数const引用,您要求编译器确保上述情况不会发生,并在编译代码时报告为错误。

答案 1 :(得分:3)

const的一大优点是它意味着它们不会改变,因为使用C ++编译器可以看到所有类型,如果它不可变但是const知道只有const方法可以被调用等等,ad它可以强制执行const方法不会改变那种状态(编译那些类的实现时)

当你使用const任何东西时,编译器可以确定它不会改变(除非可变,但我们忽略它),所以它可以对代码做一些漂亮的优化。

如果每个函数参数都是const且不可变,那么编译器知道它何时使用那些函数,该对象在调用之前和之后处于相同的状态,因此寄存器中的任何值都不需要刷新(尽管寄存器分配很久以后。)

Const很好,如果有人是“总妓女”,这是一种恭维。

所以回答这个问题,私有意味着只有朋友和班级才能改变状态,const意味着没有人可以(除非可变)。

很难给出一个例子,因为优化只发生在编译器可以在/ a标准之后通过完全相同的程序状态进行PROVE时,就好像它写了你写的那样。

class State {
private:
    bool state;
public:
    State() {
        state = false;
    }
    bool getState() {
        return state;
    }
    void alterState() {
        state = !state;
    }
};

void doSomething(State&);

int main(int,char**) {
    State state;
    bool before = state.getState();
    std::cout<<"The state is: "<<before<<"\n";
    doSomething(state);
    bool after = state.getState();
    std::cout<<"The state is: "<<after<<"\n";
    return 0;
}

假设doSomething在另一个文件中定义。

当我们发现编译器之后必须对state.getState()进行调用,因为它无法证明在==之后。

但是,如果我们有:

void doSomething(const State&);

int main(int,char**) {
    State state;
    bool before = state.getState();
    std::cout<<"The state is: "<<before<<"\n";
    doSomething(state);
    bool after = state.getState(); 
    /*compiler can do after=before because bool
     is a type that will produce the same result
      as if it actually did the getState*/
    std::cout<<"The state is: "<<after<<"\n";
    return 0;
}

这表明const是多么伟大。编译器知道doSomething采用常量(引用a)状态,并且由于状态没有可变字段,因此它知道状态状态因为该调用而无法改变。如果它们都在同一个文件中,它可能会看到doSomething做了什么(它没有改变状态假设)并意识到它无论如何都可以做到,现代的非常非常聪明。您希望为编译器提供每次优化的机会。

传递const引用意味着一切都可以看,但不能触摸,因此编译器必须处理的任何值都是有效的,因为它是const,它们不能改变。

记住这个: 所有优化都好像从未发生过一样,程序状态是相同的,没有秒表或调试器或反汇编程序,你无法分辨编译器做了什么。它可以优化一些变量(如果它可以证明没有副作用),打印该变量虽然会阻止它这样做,编译器将始终按照标准执行完全键入的操作