C++:按值将对象传递给相同 class 的成员函数

C++: Passing objects by value to a member function of the same class

我是 C++ 的初学者,我刚刚开始学习 OOP。在下面的程序中,我添加了相同 classes 的对象并显示了结果。但是,我无法理解这样一个事实,即如果我按值将对象传递给函数,那么调用函数中的变化是如何反映的。 addNumbers() 函数需要 class Complex 的两个对象,用于调用函数的对象 (c3.addNumbers(c1, c2)) 被隐式传递给函数,但是 [=14 的值如何=] 和 c3.imaginary 在调用函数中受到影响,因为 addNumbers() 无法访问它们在内存中的 "location"。任何帮助将不胜感激!

提前致谢!

class complex {
private:
    int real;
    int imaginary;

public:
/* Using member initializers to assign values to members */    
    complex()
        : real(0)
        , imaginary(0)
    {}

    void readData(int x, int y);

    void printData();

    void addNumbers(complex, complex);
};     

void complex::readData(int x, int y)
{
    real      = x;
    imaginary = y;
}

void complex::printData()
{
    cout << real << "+" << imaginary << "i" << endl;
}   

void complex::addNumbers(complex c1, complex c2)
{
    real      = c1.real + c2.real;
    imaginary = c1.imaginary + c2.imaginary;
}

int main(void)
{
    complex c1, c2, c3;
    c1.readData(-5,17);
    c2.readData(11,7);
    c3.addNumbers(c1,c2);
    c3.printData();

    return 0;
}

当您调用 c3.addNumbers(c1, c2)) 时,addNumbers 隐含地接收指向 c3 指针 而不是 c3 的副本。该指针可以明确地与 this 关键字一起使用。

所以你的函数可以这样重写:

void complex::addNumbers(complex c1, complex c2)
{
    this->real      = c1.real + c2.real;
    this->imaginary = c1.imaginary + c2.imaginary;
}

这完全等同于您的原始 addNumbers 函数。

换句话说:每次在成员函数中使用 class 成员时,隐含的 this-> 都会添加到该成员之前;所以如果 member 是 class 成员,那么 member 总是等同于 class 成员函数中的 this->member

虚部和实部是私有属性,但可以通过成员函数(也称为对象的方法)访问。 c3.addNumbers(c1,c2)语句执行时,会等价于下面两条语句:

c3.real = c1.real + c2.real;

c3.imaginary = c1.imaginary + c2.imaginary

之所以可以访问c3.real和c3.imaginary是因为addNymbers()函数是Complexclass.

的成员

我在你的原始代码中做了一些评论来解释为什么实数和虚数在下面会受到影响。 (寻找//MABVT)

另外: 我将提供另一个有用的示例,让您进一步进步!

回顾

class complex {
private:
    int real;
    int imaginary;

public:
    /* Using member initializers to assign values to members */    
    complex()
        : real(0)
        , imaginary(0)
    {}

    void readData(int x, int y);

    void printData();

    // MABVT: You provide two complex numbers which you want to add 
    //        together!
    void addNumbers(complex, complex);
};     

void complex::readData(int x, int y)
{
    real      = x;
    imaginary = y;
}

void complex::printData()
{
    cout << real << "+" << imaginary << "i" << endl;
}   

void complex::addNumbers(complex c1, complex c2)
{
    // MABVT: Use c1.component and c2.component, add them up and store them 
    //        in this class' instance.
    real      = c1.real      + c2.real;
    imaginary = c1.imaginary + c2.imaginary;

    // MABVT: c3.real and c3.imaginary are affected at this exact location
    //        since you overwrite the values with the addition-results.
    //        Since the function addNumbers(complex, complex) is invoked
    //        on the complex instance 'c3', real and imaginary of c3 are 
    //        known in this context, and consequently you can use them.
    //
    //        To attach to your statement that the c3 instance's pointer is 
    //        implicitly passed: 
    //        Yes it is passed as the first parameter invisibly as 
    //         'complex* this'
    //
    //        So you could also write:
    //          this->real = c1.real + c2.real; (see the use of this?)
}

int main(void)
{
    complex c1, c2, c3;
    c1.readData(-5,17);
    c2.readData(11,7);
    c3.addNumbers(c1,c2);
    c3.printData();

    return 0;
}

备选方案

// Example program
#include <iostream>
#include <string>

class Complex { // Give class names capital first letter
private:
    int m_real;      // Just a recommendation: I'd like to be able to distinguish parameter for member in the identifier already!
    int m_imaginary; // Just a recommendation: I'd like to be able to distinguish parameter for member in the identifier already!

public:
    /* Using member initializers to assign values to members */    
    inline Complex()   // Inline it, if you define this class in a header and reuse it multiple times...
        : m_real(0)
        , m_imaginary(0)
    {}

    // Provide initializing constructor to be able to construct 
    // a complex number quickly. Replaces your readData(...);
    inline Complex(
        int inRealPart,
        int inImaginaryPart)
        : m_real(inRealPart)
        , m_imaginary(inImaginaryPart)
    {}

    // Getters to read the values
    inline int real()      const { return m_real; }
    inline int imaginary() const { return m_imaginary; }

    void printData();

    // Local assignment-add operator to add another complex
    // to this specific instance of complex and modify the internal
    // values. Basically what you did as the second part of addNumbers.
    Complex& operator+=(const Complex& r);
};     

void Complex::printData()
{
    std::cout << m_real << "+" << m_imaginary << "i" << std::endl;
}   

// Member add-assign operator definition adding this instance and another instance 'r' by adding up the values and storing them in the instance this operator is called on.
Complex& Complex::operator +=(const Complex& r) 
{ 
    std::cout << "Local" << std::endl;

    this->m_real      += r.real();
    this->m_imaginary += r.imaginary();

    return *this;
}

// Static global operator+ definition, taking two values and creating a 
// third, NEW one initialized with the results.
// This was the first part of addNumbers
static Complex operator+(const Complex& l, const Complex& r) { 
   std::cout << "Static Global" << std::endl;

   return Complex(
            (l.real()      + r.real()), 
            (l.imaginary() + r.imaginary())
          );
}

int main(void)
{ 
    // Same as before
    Complex c1(-5, 17);
    Complex c2(11, 7);
    Complex c3(1, 2);

    // Test output
    c1.printData();
    c2.printData();
    c3.printData();

    std::cout << std::endl;

    Complex  c3 = (c1 + c2);           // Calls static global and c3 is overwritten with the result. Exactly like your addNumbers call
    c1 += c2;                          // instance local, will change c1's internal values ( see print out below )
    Complex  c5 = ::operator+(c1, c2); // Static global, c5 is initialized with the result. Exactly like your addNumbers call

    std::cout << std::endl;

    c1.printData();
    c2.printData();
    c3.printData();
    c5.printData();

    return 0;
}

对于初学者来说,这应该足够了。

一些解释

静态全局与局部运算符重载

阅读主题:http://en.cppreference.com/w/cpp/language/operators

您使用的所有运算符(+、-、*、/、%、+=、-=、...)都只是函数,它们是为原始类型预定义的,由 libstd 为 STD 类型提供。

不过你可以 override/define 他们。

我用两种方式做到了:

Static global operator+:

Accepts two arbitrary Complex instances and adds their components. Finally a NEW instance is created and initialized with the results.

Basically this is just a static function, which is linked to "+" by the compiler.

并且:

Local member operator+=:

Accepts another instance of Complex and adds its component values to the component values of the instance the operator is called on: `l += r -> Called on l, whose values will be modified by adding the values of r'

All op-assignment operators (+=, -=, *=, /=, etc...) must be defined within the class and can neither be global, nor static.

常量类型&

正在阅读有关 const 的更多信息:https://www.cprogramming.com/tutorial/const_correctness.html

对任何类型的实例的常量引用将为您确保两件事:

  1. &:你只复制地址,但这样你的函数可以改变所有public值或调用大多数函数。
  2. const: 实例不可修改,什么也不能改变

组合起来意味着:您不必复制实例(按值传递),而只需提供它的地址引用(按引用传递)。通常这会提高性能,尤其是当您传递大型和复杂的对象时。