【问题标题】:How do I overload two different I/O operator in C++如何在 C++ 中重载两个不同的 I/O 运算符
【发布时间】:2016-03-07 02:57:59
【问题描述】:

我有这样的课:

MyClass {
public:
    int a;
    unsigned char b,c,d;
    size_t e,f;
    double g, h;
    friend ostream& operator<< (ostream& os, const MyClass& mc) {
        os<<mc.a<<mc.b<<mc.c<<mc.d<<mc.e<<mc.f<<mc.g<<mc.h;
        return os;
    }
};

我已经重载了&lt;&lt; 运算符,但我还想要另一个&lt;&lt; 案例,如何重载两个不同的&lt;&lt; 运算符?

我是这么想的:

MyClass {
public:
    int a;
    unsigned char b,c,d;
    size_t e,f;
    double g, h;
    friend ostream& operator<< (ostream& os, const MyClass& mc, int type) {
        if(type==1){
            os<<mc.a<<mc.b<<mc.c<<mc.d<<mc.e<<mc.f<<mc.g<<mc.h;
            return os;
        } else if(type==2){
            os<<mc.a<<mc.c<<mc.d<<mc.e<<mc.g;
            return os;
    }
};

但没用,Too many arguments for this operator function

我该怎么做?

【问题讨论】:

  • 你到底想做什么?第二次重载会解决什么问题?你能告诉我们你想如何使用输出运算符吗?另外,请阅读the XY problem,这与您的问题非常相关,它的当前形状。
  • 嗯,您的链接很有帮助。在我的问题中,我想分两个阶段写MyClass。在第一阶段,我需要存储一些额外的信息,如bfh,然后,我合并所有临时文件并只存储acdeg。因此,如果有两个&lt;&lt; 案例会很方便。谢谢。
  • MyClass 的“type1”和“type2”实例,它们的区别有多大?是否可以通过创建两个类来解决,MyClass2 具有 acdg 成员,然后是 MyClass1,它继承自 MyClass2 并添加了对另一个的支持会员?
  • 您的成员需要公开吗?通常使用类时,如果使用继承,它们应该是私有的或受保护的。如果需要或首选公共接口,则首选使用 Struct。如果您不需要公共接口,那么 Class 是更好的选择,并且将您的成员设为私有将需要设置和获取、更新、操作等方法,这不是问题。如果这是您想要的方向,我也许可以提供解决方案,但不是您期望的方式。我完成并提交后,您可以在下面查看我的答案。
  • @JoachimPileborg 你的想法很有启发性,我会试试的,谢谢。

标签: c++ operator-overloading overloading


【解决方案1】:

我已经修改了这个类,并决定保留原始答案以供参考。现在这个版本在设计上更加优雅了一点;但是,如果我使用我的错误处理程序/记录器库,它可能会更干净,但是将它们包含在这个答案中是很大的规模适合这里,但主要功能将设置在 try catch 块中,并且我的错误如果错误很严重,处理程序和记录器会抛出错误并退出程序,或者如果值无效但程序的操作仍处于可接受的状态以继续,则将消息记录到控制台窗口或文件。

这个类有点臃肿,错误消息通常不需要在那里,但用作演示以显示正确的程序逻辑流程。

这是我向user1024 声明的更新的类和主函数,它解决了基于字段值而不是调用哪个函数来设置布尔标志变量的问题。有了这个,他现在可以拥有未初始化到类的默认值,然后用相同的默认值初始化类。类的状态现在基于函数调用而不是成员值。

Temp.h

#ifndef TEMP_H
#define TEMP_H

class Temp {
    friend std::ostream& operator<<(std::ostream& os, const Temp& t);
private:
    int m_a, m_b, m_c;
    double m_d, m_e, m_f;

    bool m_isInitialized;
    bool m_updated;

    const std::string m_strInitMessage = std::string( "First stage values must be initalized before calling this funciton.\n" );
    const std::string m_strUpdateMessage = std::string( "setUpdateStage needs to be called first before modifying this value.\n" );
public:
    Temp();
    Temp( int a, int b, int c );
    Temp( int a, int b, int c, double d, double e, double f );

    void    setInitialStage( int a, int b, int c );
    void    setUpdateStage( double d, double e, double f );

    bool    isInitialized() const;
    bool    isUpdated() const;

    int     getA() const;
    int     getB() const;
    int     getC() const;

    // These Are Updating Functions Not Setting Functions setInitialStage Must Be Called First
    void    updateA( int a );
    void    updateB( int b );
    void    updateC( int c );

    double  getD() const;
    double  getE() const;
    double  getF() const;

    // These Are Updating Functions Not Setting Functions Both setInitialStage & setUpdateStage Must Be Called First
    void    updateD( double d );
    void    updateE( double e );
    void    updateF( double f );

private:
    // Helper Function
    bool   testStages();

}; // Temp

#endif // TEMP_H

Temp.cpp

#include "stdafx.h"
#include "Temp.h"

std::ostream& operator<<( std::ostream& os, const Temp& t ) {
    if ( t.isUpdated() ) {
        os << t.getA() << " " << t.getB() << " " << t.getC() << " "
           << t.getD() << " " << t.getE() << " " << t.getF() << std::endl;
        return os;
    } else {
        os << t.getA() << " " << t.getB() << " " << t.getC() << std::endl;
        return os;
    }

} // operator<<

Temp::Temp() :
m_a( 0 ),
m_b( 0 ),
m_c( 0 ),
m_d( 0 ),
m_e( 0 ),
m_f( 0 ),
m_isInitialized( false ),
m_updated( false ) {
} // Temp

Temp::Temp( int a, int b, int c ) :
m_a( a ),
m_b( b ),
m_c( c ),
m_d( 0.0 ),
m_e( 0.0 ),
m_f( 0.0 ),
m_isInitialized( true ),
m_updated( false ) {
} // Temp

Temp::Temp( int a, int b, int c, double d, double e, double f ) :
m_a( a ),
m_b( b ),
m_c( c ),
m_d( d ),
m_e( e ),
m_f( f ),
m_isInitialized( true ),
m_updated( true ) {
} // Temp

void Temp::setInitialStage( int a, int b, int c ) {
    // Do Nothing With 2nd Stage Variables And Update Flag

    if ( !m_isInitialized ) {
        m_a = a;
        m_b = b;
        m_c = c;
        m_isInitialized = true;
    } else {
        // Do not Reinitalize
        std::cout << "Initial stage values are already initialized, please use the individual update functions.\n";
        return;
    }   
} // setInitialStage

void Temp::setUpdateStage( double d, double e, double f ) {
    // Check To See If This Has Been Intialized First
    if ( !m_isInitialized ) {
        std::cout << "\nFirst Stage values must be initialized first\n";
        return;
    } else {
        if ( !m_updated ) {
            // Do nothing with Initial Values
            m_d = d;
            m_e = e;
            m_f = f;
            m_updated = true;
        } else {
            // Do Not Reinitalize
            std::cout << "Update stage values have already been initialized, please use the individual update functions.\n";
            return;
        }
    }
} // setUpdateStage 

bool Temp::isInitialized() const {
    return m_isInitialized;
} // isInitialized

bool Temp::isUpdated() const {
    return m_updated;
} // isUpdated

int Temp::getA() const {
    if ( !m_isInitialized ) {
        std::cout << "m_a has not been initialized\n";
        return 0;
    }
    return m_a;
} // getA

int Temp::getB() const {
    if (!m_isInitialized) {
        std::cout << "m_b has not been initialized\n";
        return 0;
    }
    return m_b;
} // getB

int Temp::getC() const {
    if ( !m_isInitialized ) {
        std::cout << "m_c has not been initialized\n";
        return 0;
    }
    return m_c;
} // getC

void Temp::updateA( int a ) {
    if ( !m_isInitialized ) {
        std::cout << m_strInitMessage;
        return;
    }
    m_a = a;
} // updateA

void Temp::updateB( int b ) {
    if ( !m_isInitialized ) {
        std::cout << m_strInitMessage;
        return;
    }
    m_b = b;
} // updateB

void Temp::updateC( int c ) {
    if ( !m_isInitialized ) {
        std::cout << m_strInitMessage;
        return;
    }
    m_c = c;
} // updateC

double Temp::getD() const {
    if ( !m_updated ) {
        std::cout << "m_d has not been initialized\n";
        return 0;
    }
    return m_d;
} // getD

double Temp::getE() const {
    if (!m_updated) {
        std::cout << "m_e has not been initialized\n";
        return 0;
    }
    return m_e;
} // getE

double Temp::getF() const {
    if (!m_updated) {
        std::cout << "m_f has not been initialized\n";
        return 0;
    }
    return m_f;
} // getF

bool Temp::testStages() {
    if ( !m_isInitialized ) {
        std::cout << m_strInitMessage;
        return false;
    } else {
        if ( !m_updated ) {
            std::cout <<  m_strUpdateMessage;
            return false;
        }
    }   
    return true;
} // testStages

void Temp::updateD( double d ) {
    if ( !testStages() ) {
        return;
    }
    m_d = d;
} // updateD

void Temp::updateE( double e ) {
    if ( !testStages() ) {
        return;
    }
    m_e = e;
} // updateE

void Temp::updateF( double f ) {
    if ( !testStages() ) {
        return;
    }
    m_f = f;
} // update

ma​​in.cpp

#include "stdafx.h"
#include "Temp.h"

int main() {

    Temp t1;
    std::cout << "Default constructor called." << std::endl;
    std::cout << t1 << std::endl;

    // Error Cases
    std::cout << "Error Cases For Default Constructor Before setInitialStage is called:" << std::endl;
    std::cout << "---------------------------------------------------------------------" << std::endl;
    std::cout << "Trying to update a first stage value before setInitialStage is called." << std::endl;
    t1.updateA( 1 );
    std::cout << t1 << std::endl;
    std::cout << "Trying to update a second stage value before setInitialStage is called." << std::endl;
    t1.updateD( 2.3 );
    std::cout << t1 << std::endl;
    std::cout << "Trying to call setUpdateStage before m_isInitialized = true" << std::endl;
    t1.setUpdateStage( 4.5, 6.7, 8.9 );
    std::cout << t1 << std::endl;

    // 1st Stage Initialization WRT To Using A Default Constructor
    std::cout << "After setInitalStage is called" << std::endl;
    t1.setInitialStage( 1, 2, 3 );
    std::cout << t1 << std::endl;

    // Error Cases
    std::cout << "Error Cases For Default Constructor After setInitialStage is called:" << std::endl;
    std::cout << "--------------------------------------------------------------------" << std::endl;
    std::cout << "Calling setInitialStage after it has already been called." << std::endl;
    t1.setInitialStage( 4, 5, 6 );
    std::cout << t1 << std::endl;
    std::cout << "Trying to update a second stage value after setInitialStage and before setUpdateStage have been called." << std::endl;
    t1.updateD( 7.8 );
    std::cout << t1 << std::endl;

    std::cout << "Updating a first stage value after setInitialStage is called." << std::endl;
    t1.updateB( 9 );
    std::cout << t1 << std::endl;

    std::cout << "Calling setUpdatedStage." << std::endl;
    t1.setUpdateStage( 10.11, 12.13, 14.15 );
    std::cout << t1 << std::endl;

    // Error Case
    std::cout << "Error Case For Default Constructor After Both\n setInitialStage & setUpdateStage have been called." << std::endl;
    std::cout << "------------------------------------------------" << std::endl;
    std::cout << "Calling setUpdateStage after it has already been called." << std::endl;
    t1.setUpdateStage( 16.17, 18.19, 20.21 );
    std::cout << t1 << std::endl;

    std::cout << "Updating second stage value afer both setInitializeStage & setUpdateStage have been called." << std::endl;
    t1.updateF( 22.23 );
    std::cout << t1 << std::endl << std::endl;

    Temp t2( 1, 2, 3 );
    std::cout << "First stage constructor called" << std::endl;
    std::cout << t2 << std::endl;

    // Error Cases
    std::cout << "Error Cases For 1st Stage Constructor" << std::endl;
    std::cout << "-------------------------------------" << std::endl;
    std::cout << "Calling setInitialStage after using this constructor." << std::endl;
    t2.setInitialStage( 4, 5, 6 );
    std::cout << t2 << std::endl;

    std::cout << "Trying To Update Second Stage Value Before setUpdateStage is called." << std::endl;
    t2.updateD( 7.8 );
    std::cout << t2 << std::endl;

    std::cout << "Updating 1st Stage Value" << std::endl;
    t2.updateB( 9 );
    std::cout << t2 << std::endl;

    std::cout << "Calling setUpdateStage" << std::endl;
    t2.setUpdateStage( 10.11, 12.13, 14.15 );
    std::cout << t2 << std::endl;

    // Error Case
    std::cout << "Error Case For 1st Stage Constructor After setUpdateStage has been called." << std::endl;
    std::cout << "-------------------------------------------------------------------------" << std::endl;
    t2.setUpdateStage( 16.17, 18.19, 20.21 );
    std::cout << t2 << std::endl;

    std::cout << "Updating 2nd stage value." << std::endl;
    t2.updateE( 22.23 );
    std::cout << t2 << std::endl << std::endl;


    Temp t3( 1, 2, 3, 4.5, 6.7, 8.9 );
    std::cout << "Full Stage Constructor Called" << std::endl;
    std::cout << t3 << std::endl;

    // Error Cases
    std::cout << "Error Cases For Full Stage Constructor:" << std::endl;
    std::cout << "---------------------------------------" << std::endl;
    std::cout << "Calling setInitialStage" << std::endl;
    t3.setInitialStage( 10, 11, 12 );
    std::cout << t3 << std::endl;
    std::cout << "Calling setUpdateStage" << std::endl;
    t3.setUpdateStage( 13.14, 15.16, 17.18 );
    std::cout << t3 << std::endl;

    std::cout << "Updating 1st & 2nd Stage Values" << std::endl;
    t3.updateA( 19 );
    t3.updateD( 20.21 );
    std::cout << t3 << std::endl;

    std::cout << "With this design 0 is now an acceptable value." << std::endl;
    std::cout << "Updating all of t3's values." << std::endl;
    t3.updateA( 0 );
    t3.updateB( 0 );
    t3.updateC( 0 );
    t3.updateD( 0 );
    t3.updateE( 0 );
    t3.updateF( 0 );
    std::cout << t3 << std::endl;

    std::cout << "Using Default Constructor To Show That Both stageFunctions Can accept 0 as value" << std::endl;
    Temp t4;
    std::cout << "Unitialized:" << std::endl
              << t4 << std::endl;

    std::cout << "Calling setInitialStage" << std::endl;
    t4.setInitialStage( 0, 0, 0 );
    std::cout << t4 << std::endl;
    std::cout << "Calling setUpdateStage" << std::endl;
    t4.setUpdateStage( 0, 0, 0 );
    std::cout << t4 << std::endl;


    std::cout << std::endl; // Used As A Break Point Before Application End

    return 0;
} // main

这个类有 3 个构造函数,你可以通过 3 种方式创建这个类:创建一个空的未初始化版本以稍后填写所有部分,在构造时设置第 1 阶段以稍后更新第 2 阶段,最后完全设置施工的各个阶段。这也表明所有 3 个构造函数、所有初始化函数、更新函数和 getter 都通过相同的std::ostream &lt;&lt; operator 工作。它还演示了如何按特定顺序调用特定函数,还演示了何时不重复调用特定函数。我相信还有很多其他方法可以解决这个问题,但是能够以几种成功的方式完成相同的任务有其优势。

【讨论】:

  • 这个答案确实更优雅!我真的很感谢你的帮助!我会接受你的回答。
【解决方案2】:

您不能有两个具有相同签名的函数,并且您发现您不能向此运算符重载添加其他参数。

这可以编译,但我建议您找到另一种设计,因为这是可憎的:

#include <iostream>

class MyClass
{

};

std::ostream& operator<<( std::ostream& os, const MyClass& myClass ) { return os; }
std::ostream& operator<<( std::ostream& os, MyClass& myClass ) { return os; }

【讨论】:

    【解决方案3】:

    Temp.h

    #ifndef TEMP_H
    #define TEMP_H
    
    class Temp {
        friend std::ostream& operator<<(std::ostream& os, const Temp& t);
    private:
        int m_a;
        double m_b;
    
        bool m_updated;
    
    public:
        Temp();
        explicit Temp( int a, double b = 0 );
    
        int     getA() const;
        void    setA( int a );
        double  getB() const;
        void    setB( double b );
    
        bool    isUpdated() const;
    
    }; // Temp
    
    #endif // TEMP_H
    

    Temp.cpp

    #include "stdafx.h"
    #include "Temp.h"
    
    std::ostream& operator<<( std::ostream& os, const Temp& t ) {
        if ( t.isUpdated() ) {
            os << t.getA() << " " << t.getB();
            return os;
        } else {
            os << t.getA();
            return os;
        }
    
    } // operator<<
    
    Temp::Temp() :
    m_a( 0 ),
    m_b( 0 ),
    m_updated( false ) {
    } // Temp
    
    Temp::Temp( int a, double b ) :
    m_a( a ),
    m_b( b ),
    m_updated( false ) {
        if ( m_b != 0 ) {
            m_updated = true;
        } 
    } // Temp
    
    int Temp::getA() const {
        return m_a;
    } // getA
    
    void Temp::setA( int a ) {
        m_a = a;
    } // setA
    
    double Temp::getB() const {
        return m_b;
    } // getB
    
    void Temp::setB( double b ) {
        m_b = b;
        if ( m_b != 0 ) {
            m_updated = true;
        }
    } // setB
    
    bool Temp::isUpdated() const {
        return m_updated;
    } // isUpdated
    

    ma​​in.cpp

    #include "stdafx.h"
    #include "Temp.h"
    
    int main() {
        Temp t1( 3 );
    
        std::cout << "Before Updated" << std::endl;
        std::cout << t1 << std::endl << std::endl;
    
        std::cout << "After Updated" << std::endl;
        t1.setB( 4.2 );
        std::cout << t1 << std::endl << std::endl;
    
        Temp t2( 7, 12.5 );
    
        std::cout << "Updated Values During Construction" << std::endl;
        std::cout << t2 << std::endl;
    
    
        std::cout << std::endl;
    
        return 0;
    } // main
    

    std::ostream &lt;&lt; operator 不允许您传入 3 个值,它需要一个 ostream 对象和您要传递给它的对象。所以我在这里所做的是我创建了一个具有默认构造函数的类以及一个显式构造函数,其中最后一个参数是可选的。现在这也需要类维护 1 个额外的变量,一个布尔类型。此布尔类型跟踪以查看可选参数是否已在初始构造函数期间或通过更新函数或设置函数随时更新。然后当这个类对象与std::ostream &lt;&lt; operator 一起使用时,它首先检查这个布尔值是真还是假,然后从那里分支到它应该使用的流类型。

    现在,当您开始与班级合作时,您必须考虑到;更新值是否发生在两个直接阶段?还是可以一次更新一个?提前了解这一点很重要。如果您知道稍后或在第二阶段向您的课程添加或更新多个变量,那么我提供的方法将起作用并且易于管理。

    现在,如果您在两个以上的多个阶段中一次添加组件 1,那么这种方法会变得更加复杂。现在,在我展示的示例类中,这个设计过程存在一个缺陷。设计缺陷是这样的:如果 0 是m_b 的可接受答案怎么办?那么这种更新方法将不适用于这种情况,因为该类将认为它没有更新并且它不会输出第二个字段。这是需要考虑的。

    一种可能的解决方法是执行与我提供的相同的设计方法,但将所有初始值作为基本数据类型,并将在第二阶段添加的所有参数作为指向它们的指针类型。然后这样你就可以根据指针是否有值或者是nullptr来设置你的m_updated

    但此代码确实演示了一种通过单个 std::ostream &lt;&lt; operator 调用来分支不同 std::ostream &lt;&lt; operators 的方法。

    编辑

    另一种可能的解决方案是在类中使用枚举,而不是在类中使用单个布尔值:

    class SomeClass {
    public:
        enum Stage {
            S_1 = 1,
            S_2,
            S_3,
            S_LAST,
        };
    
    private:
        Stage m_stage;
    
    public:
        Stage getStage() const { return m_stage; }
    };
    

    然后当你用默认类型构造你的类时; m_stage 将设置为 S_1,然后当您执行特定的更新组时,您可以通过多个阶段并在 std::ostream &lt;&lt; operator 方法内,而不是使用带有 bool 的 if 语句,您可以使用 switch 和 case 语句基于它所在的类阶段,在 switch 语句中,默认情况可能是第一阶段或错误情况。

    【讨论】:

    • 哇,你的答案就是我想要的,m_updated 成员真的很有帮助,我可以检查m_updated 的价值并在&lt;&lt; 中做我想做的事。 SomeClass 是一种更通用的方式。谢谢!
    • @user1024 是的,如果你有类似的情况,你可能有两个以上的阶段,后面的方法适用于类枚举,并使用 switch 语句而不是使用 if 语句一个布尔变量。
    • @user1024 另外,请记住,如果您最初使用它并且 0 是 m_b 的可接受值,它将默认为 false 并且它不会显示 0 或显示该变量或场甚至存在。只是需要考虑的事情。
    • @user1024 虽然这个答案对你有用,并且考虑到你确实声明你的课程分两个阶段进行,经过深思熟虑,我倾向于这样一个事实,即我并不快乐有了这个,我可以做得更好。当我有更多的时间时;我会回到这个并发布另一个答案,同时留下这个作为参考。这将是一个类似的方法,但它的设计更优雅一点,但它只能在两阶段方法中工作。根据使用的构造函数或函数调用将设置标志而不是存储的值。
    猜你喜欢
    • 1970-01-01
    • 2018-05-01
    • 2016-03-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多