【问题标题】:Im confused about the use of an operator overload for '<<' in C++我对在 C++ 中为“<<”使用运算符重载感到困惑
【发布时间】:2020-06-21 16:58:25
【问题描述】:

对于这个例子,我将只使用两个无意义的类对象 - X 和 Y。真正重要的是 X。

X 包含几个成员函数:一个 Y 指针数组,以及一个命名 X 类型对象的标题。

在构造过程中,唯一需要的是类对象的名称。因此,它的声明就像

X newList("ListTitle")

现在,从逻辑上讲,您会认为向指针数组添加数据的方式类似于

for(int x = 0; each item passed){
 newList.Array[x] = itemPassed[x]; 

但是,我看到过一些任务要求我们以一种奇怪的方式来做这件事。我们被要求重载'

newList <<"Item 1"<<"item 2"<<"item3";

这种向类成员添加内容的方法在实际工作场景中是否合法使用?当我看着它时,它似乎在语法上并不正确,而且非常不需要。我只是想弄清楚这是否是我在学校课程之外真正使用的东西。

【问题讨论】:

    标签: c++ syntax


    【解决方案1】:

    这种向类成员添加内容的方法在实际工作场景中是否合法使用?当我看着它时,它似乎在语法上并不正确,而且非常不需要。我只是想弄清楚这是否是我在学校课外实际使用过的东西。

    C++ 的某些功能源于overload operators 的可能性。一如既往,强大的力量伴随着巨大的责任。操作符可以被重载来做显而易见的事情,例如没有人会对这样的代码感到惊讶

    Matrix a{ {1,2},{2,3} };
    Matrix c = a + a;
    

    也许您甚至没有注意到这里的+ 是一个自定义运算符。 C++ 本身不能添加两个矩阵。运算符重载的一个缺点是你可以重载它们来做任何事情,有时它并不那么明显。良好的运算符重载与导致代码混乱的运算符之间的界限很窄。

    关于您的具体示例,这是一种您也可以在其他地方找到的模式。 Eigen 是一个著名的代数库。他们文档中的一个例子是:

    #include <iostream>
    #include <Eigen/Dense>
    using namespace Eigen;
    int main()
    {
      Matrix2d a;
      a << 1, 2,
           3, 4;
      MatrixXd b(2,2);
      b << 2, 3,
           1, 4;
      std::cout << "a + b =\n" << a + b << std::endl;
      std::cout << "a - b =\n" << a - b << std::endl;
      std::cout << "Doing a += b;" << std::endl;
      a += b;
      std::cout << "Now a =\n" << a << std::endl;
      Vector3d v(1,2,3);
      Vector3d w(1,0,0);
      std::cout << "-v + w - v =\n" << -v + w - v << std::endl;
    }
    

    注意使用&lt;&lt; 运算符来设置矩阵元素。他们添加了一些逗号运算符 voodoo,但 &lt;&lt; 运算符的使用与您应该实现的非常相似。

    这种向类成员添加东西的方法在实际工作场景中是否合法使用?

    是的。使用operator&lt;&lt; 将元素“插入”到对象中是完全合法的。您将在各种库中看到运算符重载的这种用法。如果以这种方式使用运算符当然需要适当的文档,因为了解 C++ 语言不足以了解运算符在特定上下文中的实际含义。

    【讨论】:

      【解决方案2】:

      您可以创建一个函数来将新的 Y 对象添加到 X 数组。每次调用该函数时,它都会返回相同的 x 对象,因此可以使用相同的 x 再次调用该函数。

      X& operator<<(X& x, const char * s){
        x.Array.push_back(new Y(s));
        return x;
      }
      

      (push_back 是您使用 std::vector 或 emplace_back 添加项目的方式)

      这类似于 std::cout 的工作方式。

      【讨论】:

      • 这实际上似乎是一种非常可行的实现方式,我非常感谢这里的澄清。因为使用
      【解决方案3】:

      这种向类成员添加内容的方法在实际工作场景中是否合法使用?

      可能不会。无论如何,大多数情况下都不会。

      队列和堆栈可能会用到它,其中&lt;&lt; 表示添加到队列或堆栈,&gt;&gt; 表示从队列或堆栈中读取和删除。

      其他可能的用途是,如果您找到write your own stream 的原因,您可以将数据存储为std::vector,并使用&lt;&lt; 运算符向其中添加字符数据。

      不过,一般来说,我不建议使用&lt;&lt; 运算符来执行此类操作。它很容易与std::ostreamstd::istream&lt;&lt;&gt;&gt; 运算符混淆。大多数阅读您的代码的人很容易误解它。另外,无论如何都有更好的方法来初始化数据,例如initializer lists

      【讨论】:

        【解决方案4】:

        从技术上讲,按照您的意愿工作是完全可行的,Quentin 的回答解释了如何做到这一点。

        更常见的做法是使用operator&lt;&lt; 作为数据输出的流插入器(到控制台、文件,但在字符串流的情况下也在内存中)。但是使用类似流的界面,您的用户可以合理地期望您始终如一地实现流行为,以便他们可以使用一些格式选项,例如setw 甚至endl

        但是这种类似流的用法是一个例外,而不是规则。它不符合移位运算符&lt;&lt; 的通常语义,预计不会对其操作数产生任何副作用。因此,除非 X 代表某种类型的流,否则我建议不要使用它并建议使用方法链接技术:

        X.push(Y1).push(Y2).push(Y3); 
        

        您将完全按照 Quentin 建议的 operator&lt;&lt; 定义 push()(或 appendadd),除了函数的名称。

        【讨论】:

        • 是的,我更习惯于将它用作数据的流插入器,而不是用于将事物分配给对象。 X 不代表一个流,在这种情况下它是一个类,我们使用它来将数据分配给一个成员数组。所以很奇怪看到它用于除了数据输出之外的任何东西
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-08-21
        • 1970-01-01
        • 2021-09-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-06-12
        相关资源
        最近更新 更多