【问题标题】:Why my overloaded operator is not working for derived class为什么我的重载运算符不适用于派生类
【发布时间】:2022-01-11 18:23:36
【问题描述】:

我试图学习 C++ PL 中的运算符重载。我做了一个如下所示的练习。我想要做的是为每个派生类重载

班级员工:

class Employee {
public:
    string name;
    int id;
    int exp_level;
    double salary;

    Employee() {
        this->name = "";
        this->id = 0;
        this->exp_level = 0;
        this->salary = 0.0;
    }

    ~Employee() {
        //WTF
    }

    virtual void calculateSalary() {
        //CODE
    }
    
    virtual void registerX() {
        //CODE
    }

    friend ostream& operator<<(ostream& os, const Employee& e) {
        os << e.name << " " << e.exp_level << " " << e.id << " " << e.salary << endl;
        return os;
    }
};

技术类:

class Technical : public Employee {
public:
    string profession;

    Technical() {
        this->profession = "";
    }

    ~Technical() {

    }

    virtual void calculateSalary() {
        //CODE
    }

    virtual void registerX() {
        //CODE
    }

    friend ostream& operator<<(ostream& os, const Technical& e) {
        os << e.name << " " << e.exp_level << " " << e.id << " " << e.salary << "Technical" << endl;
        return os;
    }
};

类工程师:

class Engineer : public Employee {
public:
    Engineer() {

    }

    ~Engineer() {

    }

    virtual void calculateSalary() {
        //CODE
    }

    virtual void registerX() {
        //CODE
    }

    friend ostream& operator<<(ostream& os, const Engineer& e) {
        os << e.name << " " << e.exp_level << " " << e.id << " " << e.salary << "Engineer" << endl;
        return os;
    }
};

主要方法:

int main()
{
    Employee* e = new Employee();
    Employee* t = new Technical();
    Employee* ee = new Engineer();

    cout << *e << endl;
    cout << *t << endl;
    cout << *ee << endl;    
}    

输出:

 0 0 0

 0 0 0

 0 0 0

【问题讨论】:

  • 您遇到了什么错误或错误?
  • 如果&lt;&lt; 的第二个操作数是Employee&amp;,编译器的唯一选择是实现operator&lt;&lt;,它将Employee const&amp; 作为第二个参数。这适用于您打印的所有 3 个对象...
  • 给你的所有类一个虚函数std::ostream put(std::ostream &amp; os) const;,并在Employee的运算符重载中使用那个虚函数。这应该足够了。
  • @Bathsheba 我只是懒惰的 ATM ;-P
  • 看看能不能申请Print Derived class object using a vector of Base class pointers referring this object。 Sam 在他的回答中展示的基本上与 πάνταῥεῖ 描述的方法相同。

标签: c++ class oop operator-overloading class-hierarchy


【解决方案1】:

C++ 根据函数参数的静态类型选择最佳重载,因为在这种情况下类型是EmployeeEmployeeoperator&lt;&lt; 被调用.

如果你希望它在你有一个与其动态类型不匹配的静态类型指针/引用时调用正确的版本,你将不得不使用虚函数或使用dynamic_casts/typeid 来检查具体的运行时类型(虚拟函数是最干净的方法恕我直言)

示例:godbolt

class Employee {
public:
    virtual ~Employee() = default;

    friend std::ostream& operator<<(std::ostream& os, const Employee& e) {
        return e.put(os);
    }

protected:
    virtual std::ostream& put(std::ostream& os) const {
        os << "Employee!";
        return os;
    }
};

class Technical : public Employee {
protected:
    std::ostream& put(std::ostream& os) const override {
        os << "Technical Employee!";
        return os;
    }
};

class Engineer : public Employee {
protected:
    std::ostream& put(std::ostream& os) const override {
        os << "Engineer Employee!";
        return os;
    }
};

int main() {
    Employee* e = new Employee();
    Employee* t = new Technical();
    Employee* ee = new Engineer();

    std::cout << *e << std::endl;
    std::cout << *t << std::endl;
    std::cout << *ee << std::endl;

    delete ee;
    delete t;
    delete e; 
}

会导致:

Employee!
Technical Employee!
Engineer Employee!

还要记住,一旦你在一个类中至少有 1 个虚函数,那么析构函数几乎肯定也是虚函数。

例如:

Employee* e = new Technical();
delete e;

如果析构函数不是虚拟的,则只会调用~Employee(),但不会 ~Technical()

因此,每当您想通过指向其基类之一的指针删除对象时,析构函数必须是虚拟的,否则它是未定义的行为

【讨论】:

  • 感谢您的回答。它现在有效,但我想学习另一种解决方案。我的讲师对我说:“这个重载的插入运算符 (
  • @Roxox 这基本上是我发布的解决方案,只是将“put”函数提取到接口类中 - 例如像这样的东西应该满足它:godbolt - 这允许实现Printable 接口的每个类与流一起使用。
【解决方案2】:

由于这些声明

Employee* e = new Employee();
Employee* t = new Technical();
Employee* ee = new Engineer();

表达式*e*t*ee的静态类型为Employee &amp;。所以运营商

friend ostream& operator<<(ostream& os, const Employee& e)

为所有三个对象调用。

使友元运算符

virtual std::ostream & out( std::ostream & ) const;

并定义(唯一的)友元运算符,如

friend ostream& operator<<(ostream& os, const Employee& e)
{
    return e.out( os );
}

每个派生类都需要重新定义虚函数out

【讨论】:

  • @fabian 是的,它是一个引用类型,但在问题的上下文中这并不重要。
  • 感谢您的回答。它现在有效,但我想学习另一种解决方案。我的讲师对我说:“这个重载的插入运算符 (
  • @Roxox 很抱歉,我不知道你的导师是什么意思。
  • 这个重载的插入操作符 (只有在静态已知类型时才选择正确的重载。在处理具有虚函数的类层次结构时,这种情况很少发生。 并创建一个接口类(就像我们在课程中所做的那样)我们不知道您在课程中做了什么并提供功能,以便所有员工都可以使用重载的插入运算符 打印到 ostream,这是解决方案以标准方式所做的。如果你想像在课程中那样做,你应该教我们课程的相关部分。
猜你喜欢
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 2021-07-03
  • 1970-01-01
  • 2019-09-02
  • 2012-03-09
  • 2014-10-19
相关资源
最近更新 更多