如何在C ++中重载两个不同的I / O操作符

时间:2016-03-07 02:57:59

标签: c++ operator-overloading overloading

我有一个这样的课程:

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;
    }
};

我已经重载了<<运算符,但我还想要另一个<<个案例,我怎样才能重载两个不同的<<运算符?

我想到了:

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

我该怎么办?

3 个答案:

答案 0 :(得分:1)

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

这是编译,但我建议你找到另一种设计,因为这是令人厌恶的:

#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; }

答案 1 :(得分:1)

<强> 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

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

现在,当你开始上课时,你必须考虑到;更新的值是在两个直接阶段发生的吗?或者他们可以一次更新一个?这对于提前了解非常重要。如果您知道将在以后或第二阶段向您的班级添加或更新多个变量,那么我提供的方法将起作用并且管理起来很简单。

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

可能的解决方法是使用我提供的相同设计方法,但要将所有初始值作为基本数据类型,并将要在第二阶段添加的所有参数作为指向它们的指针类型。然后,通过这种方式,您可以根据指针是否具有值或m_updated来设置nullptr

但是,此代码确实展示了一种通过单个std::ostream << operators调用来分支不同std::ostream << operator的方法。

修改

另一种可能的解决方案,而不是在你的类中使用单个bool,就是在你的类中使用这样的枚举:

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 << operator方法中执行多个阶段,而不是使用带有bool的if语句,您可以使用switch和case语句基于它所在的类阶段,并且在switch语句中,默认情况可以是,第一阶段或错误情况。

答案 2 :(得分:1)

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

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

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

<强> 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

<强>的main.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种方式创建这个类:创建一个空的未初始化版本以便以后填写所有部分,将第一个阶段设置为构建以便稍后更新第二个阶段,最后完全设置建设的所有阶段。这也表明所有3个构造函数,所有初始化函数,更新函数和getter都在使用相同的std::ostream << operator。它还演示了如何以特定顺序调用特定函数,并演示何时不重复调用特定函数。我相信还有很多其他方法,但能够以几种成功的方式看到同样的任务有其优点。