1

这是来自http://www.learncpp.com/cpp-tutorial/92-overloading-the-arithmetic-operators/的一些代码

我一直在研究几个自学网站和浏览论坛的操作员超载。我问了一个关于使用不同方法重载的问题,我现在明白了。问答链接在这里当它没有被定义时,它是如何返回一个值的?

但是,这个方法有参考我不明白。我尝试过使用不同的方法来实现代码,并且发现有些方法不太复杂。

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

这些问题是在您阅读代码时提出的,但这是我的三个问题。
(问题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;
}
4

2 回答 2

4

也许以下内容更具说明性:当您计算总和时,您的代码相当于:

Cents cCentsSum = operator+(cCents1,cCents2);

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

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

这个免费功能现在需要实现,但是你访问c1.m_cents的是 ,也就是private. 为了允许这样做,您需要声明方法 a friend,否则您编写的自由函数会产生访问错误。

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

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

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

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

Cents cCentsSum = cCents1+cCents2;

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

于 2013-10-08T20:28:26.870 回答
3

const 的最大好处是它们不会改变,因为使用 C++ 编译器可以看到所有类型,如果它不是可变的但 const 它知道只能调用 const 方法,因此它可以强制 const 方法不更改该状态(在编译这些类的实现时)

当您使用 const 任何东西时,编译器可以确保它不会改变(除非可变,但让我们忽略它),因此它可以对代码进行一些漂亮的优化。

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

const 很好,如果有人是“完全的 const 妓女”,那是一种恭维。

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

很难举一个例子,因为优化只有在编译器可以证明遵循 /a 标准时才会发生,结果代码通过完全相同的程序状态,就好像它做了你写的一样。

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 在另一个文件中定义。

当我们发现 after 编译器必须调用 state.getState() 因为它不能证明 before == after。

但是,如果我们有:

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 的引用)State,并且由于 state 没有可变字段,它知道 State 状态不会因为该调用而改变。如果它们都在同一个文件中,它可能会看到 doSomething 做了什么(假设它不会改变状态)并意识到它无论如何都可以做到,现代的非常聪明。你想给编译器每一个优化的机会。

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

记住这一点:所有优化工作都好像它们根本没有发生过一样,程序状态是相同的,没有秒表、调试器或反汇编器,你无法判断编译器做了什么。它可能会优化一些变量(如果它可以证明没有副作用),打印该变量虽然会阻止它这样做,但编译器将始终按照标准执行您键入的内容。

于 2013-10-08T20:10:21.220 回答