Reputation: 1052
I have a class like this:
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;
}
};
I have overloaded the <<
operator, but i also want another <<
case, how can i overloaded two different <<
operator?
I thought this:
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;
}
};
But didn't work, Too many arguments for this operator function
.
How do i do?
Upvotes: 2
Views: 83
Reputation: 7915
I have modified this class and decided to keep the original answer for reference. Now this version is a little more elegant in design; however it could be much cleaner if I was using my error handler / logger library, but to include them with this answer is to large of a scale to fit here, but the main function would be set in a try catch block, and my error handler and logger would work to throw errors and quit program if errors were critical, or to log messages to either console window or file if the values are invalid but the operation of the program is still within an acceptable state to continue.
This class is a little bloated with error messages that normally wouldn't need to be there, but are used as demonstration to show proper program logic flow.
Here is the updated class and main function that I stated to user1024
that addresses the problem of the bool flag variable being set based on the fields value instead of which function was called. With this now he can have default values that are not initialized to the class, and then initialize the class with the same default values. The state of the class is now based on function calls and not member values.
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
With this class having 3 Constructors, You can create this class in 3 ways: Creating an empty uninitialized version to fill out all parts later, Have the 1st Stage set upon Construction to update the 2nd stage later, and finally to fully set all stages upon construction. And this also demonstrates that all 3 constructors, all initializing functions, updating functions and getters are working through the same std::ostream << operator
. It also demonstrates how specific functions need to be called in specific order and also demonstrates when to not call a specific function duplicate times. I am sure there are many other approaches to this, but being able to see the same task done in several successful ways has its advantages.
Upvotes: 1
Reputation: 7915
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
The std::ostream << operator
won't allow you to pass in 3 values, it takes an ostream object, and the object you want to pass to it. So what I did here is I created the class to have a default constructor along with an explicit constructor where the last parameter is optional. Now this also requires the class to maintain 1 extra variable, a Boolean type. This Boolean type tracks to see if the optional parameter has been updated at anytime either during initial constructor or through an update function or a setting function. Then when this class object is used with the std::ostream << operator
it first checks to see if this Boolean is true or false then from there it branches to which stream type it should use.
Now when you begin to work with your class you have to take into consideration; are the updated values happening in two direct stages? Or can they be updated one at a time? This is important to know upfront. If you know that several variables will be added or updated later or in a second stage to your class then the method I provided will work and it is simple to manage.
Now if you are adding components 1 at a time in multiple stages beyond two then this approach becomes more complex. Now there is one flaw to this design process within the sample class that I have shown. The design flaw is this: what if 0 is an acceptable answer for m_b
? Then this updating approach will not work for that case, because the class will stand that it wasn't updated and it will not output the second field. This is something to take into consideration.
A possible work around would be to do the same design method as I have provided, but to have all of your initial values as basic data types, and all of the parameters to be added in the second stage to be pointers to their types. Then this way you can set your m_updated
based on if the pointer has a value or is nullptr
.
But this code does demonstrate one way to branch different std::ostream << operators
with a single std::ostream << operator
call.
EDIT
Another possible solution instead of using a single bool in your class would be to use an enumeration within your class like this:
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; }
};
Then when you constructor your class with default types; m_stage would be set to S_1, then as you do specific groups of updates, you can go through multiple stages and within the std::ostream << operator
method, instead of using an if statement with a bool, you can use a switch and case statement based on the classes stage it is in, and within the switch statement the default case could either be, the first stage or an error case.
Upvotes: 1
Reputation: 2265
You cannot have two functions with the same signature, and as you discovered you cannot add additional arguments to this operator overload.
This compiles, but I reccommend you find another design as this is an abomination:
#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; }
Upvotes: 1