【问题标题】:Why i can't call base class operator>> in derived class operator>>?为什么我不能在派生类运算符>>中调用基类运算符>>?
【发布时间】:2021-07-03 13:59:07
【问题描述】:

假设我们有 2 个类:

class base{

protected:
    char first_name;
public: 
    /// constructors , destructor, operator= overloading ...
    
    friend istream& operator >> (istream& in, base &client1) 
    {
        char f_name[1001];
        cout << "First Name: ";
        in >> f_name;
        client1=base(f_name); /// class parameterised constructor;
         
        return in;
    }
};

class derived: public base{

protected:
    char last_name[1001];
public: 
    /// constructors , destructor, operator= overloading ...
    
    friend istream& operator >> (istream& in, derived &client2) 
    {
        char l_name[1001], f_name[1001];
       
        in >> (base) client2; /// it's not working like it dose in operator<<..
        cout << "Last Name: ";
        is >> l_name;
        client2=derived(f_name, l_name); /// class parameterized constructor, 2 parameters because it use base constructor as well;
         
        return in;
    }
};

我重载了一些输出运算符(中调用运算符我不知道这对于操作员>中调用基类运算符>>时,它给了我错误:

" 没有匹配函数调用 'operator>>(std::istream&, abonament)'| "

同样,如果我在 operator

friend ostream& operator << (ostream& out, derived &client) 
    {
       
        out << (base) client; /// it's working;
       
        out << client.last_name;
         
        return in;
    }

是否可以在派生类运算符中调用基类运算符>>> 为什么会发生这种情况以及如何解决? (我希望我不必在派生类运算符>>中再次重写基类运算符>>中的相同行)

【问题讨论】:

  • char first_name; 可能会带来悲伤。很少有人有一个字母的名字。
  • 警告:(base) client2;slice。想一想,这可能是你的问题。
  • 投射到参考(base&amp;)client2;.
  • 我通常强调可重现性,但实际上两者同等重要。如果您提供了大量代码,那么您并没有真正尝试隔离问题。如果在运行给定代码时问题无法重现,则更难正确检查您的错误并证明提出的答案确实解决了问题。
  • minimal reproducible example 的真正价值在于它是一种强大的调试技术。通常,制作 MRE 的行为会减少 bug 周围的噪音,以至于您可以在完成 MRE 之前很久就看到并发现 bug。如果您在问题编写过程的早期进行 MRE,您通常可以停止编写问题。

标签: c++ class input output overloading


【解决方案1】:

您已成为object slicing 的牺牲品。您可以通过转换为参考来解决眼前的问题。

in >> (base) client2;

真正发生的是一个新的临时Baseclient2 组成,丢弃了derived 添加的所有成员。这是合法的。编译器会感到不安,因为临时的Base 不能用作base &amp;client1 参数的参数。临时变量就像听起来一样。它们的时间不长,所以引用一个是编译器阻止你犯的错误。如果它是合法的,临时变量将被Base&gt;&gt; 操作符修改,然后立即超出范围,然后才能使用读入的内容。

in >> (base&) client2;

不切片也不做临时变量。 client2 已成功更新。不幸的是,当

client2=derived(f_name, l_name);

用未初始化的变量l_name覆盖它。

因此需要进行更多更改。

不要使用构造函数来完全重新创建和重新分配您正在读取的对象。而是直接读入对象的成员变量。

例子:

class base{

protected:
    char first_name[1001]; // need more than one character
public: 
    /// constructors , destructor, operator= overloading ...
    
    friend istream& operator >> (istream& in, base &client1) 
    {
        // char f_name[1001]; don't need. Read into member
        cout << "First Name: ";
        in >> client1.first_name; // read directly into member
        // client1=base(f_name); don't need. Work done above
         
        return in;
    }
};

class derived: public base{

protected:
    char last_name[1001];
public: 
    /// constructors , destructor, operator= overloading ...
    
    friend istream& operator >> (istream& in, derived &client2) 
    {
        //char l_name[1001], f_name[1001]; don't need. Use member variables
       
        in >> (base&) client2; // client was being sliced. explanatory link above
        //         ^ fixed with reference to allow polymorphism                             
        cout << "Last Name: ";
        in >> client2.last_name; // fixed typo. reading directly into member
        // client2=derived(f_name, l_name); don't need. Work done above
         
        return in;
    }
};

旁注:这是对继承的滥用。这实际上是说姓氏is-a 名字。它不是。阅读 Liskov Substitution 原则以获得一个好的规则,以帮助确定何时继承是有意义的。这是一些入门读物:What is an example of the Liskov Substitution Principle?

旁注:&gt;&gt; 将一个以空格分隔的标记(一个单词)读入给定的char 数组。这里有两个问题。最重要的是它不知道什么时候停止。 1001 字符数组只能部分缓解这种情况。用户需要长时间打字才能溢出缓冲区,但不要怀疑人们这样做是为了好玩或盈利。改用std::string,这个问题就消失了。第二个问题是整个一个词的事情。这个解析器无法处理像“Billy Bob”或“von Doom”这样的名字,幸运的是这个姓氏的所有者是虚构的,因为众所周知他是无情的。

【讨论】:

    猜你喜欢
    • 2019-09-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-05
    • 2012-06-06
    • 1970-01-01
    • 2012-08-22
    • 1970-01-01
    相关资源
    最近更新 更多