What are the consequences of "screwing up" the assignment operator? - c++

I want to have double values with a name and units, but I want to use them as if they were simple doubles. For example I want to use them like this:
int main(){
NamedValue a("a","m");
NamedValue b("b","m");
NamedValue c("c","m^2");
a = 3;
b = 5;
c = a*b;
std::cout << a << b << c << std::endl;
return 0;
}
and the output should be:
a = 3 m
b = 5 m
c = 15 m^2
I came up with this solution:
class NamedValue {
public:
NamedValue(std::string n,std::string u) : name(n),units(u){}
const std::string name;
const std::string units;
void operator=(double v){this->value = v;}
void operator=(const NamedValue& v){this->value = v.value;}
operator double() const { return value; }
private:
double value;
};
std::ostream& operator<<(std::ostream& stream,const NamedValue& v) {
stream << v.name << " = " << (double)v << " " << v.units << std::endl;
return stream;
}
Unitl now it works nicely, but I am mainly concerned about the assignment operator void operator=(const NamedValue& v){this->value = v.value;} that is not exactly doing, what one normally would expect of an assignment operator.
Are there any bad consequences that I will face?
I am thinking about things like passing an object as parameter and stuff like that. However, a function like this
NamedValue test(NamedValue x){return x;}
works without problems, as there is no assignment involved (only copy constructor). Am I missing something? Is there anything else I should be aware of?
ps: Just in case you wonder, at the moment I do not care about checking the units when doing calculations.

The assignment operator is totally fine. The only thing unusual about it is that you did not return the object so the operator calls cannot be chained. Otherwise, it's a completely normal operator.

Related

Operator overloading (object addition)

#include "stdafx.h"
#include "iostream"
using namespace std;
class complex
{
int real;
int img;
public:
complex(int x = 0, int y = 0)
{
cout << "Inside Prama" << endl;
real = x;
img = y;
}
complex operator+(complex x)
{
complex temp;
temp.real=real + x.real;
temp.img=img + x.img;
return temp;
}
void display()
{
cout << real << ' ' << img << endl;
}
};
int main()
{
class complex c1(5,6), c2(7,8),c3(7,7),c4;
c1.display();
c2.display();
c3.display();
c4 = c1+c2+c3;
c4.display();
return 0;
}
for above operator overloading code following is the output:
why that parameterized constructor is called at the time of adding of the objects.I am not getting the reason for that
Just summarizing comments...
Here
complex operator+(complex x)
{
complex temp;
temp.real=real + x.real;
temp.img=img + x.img;
return temp;
}
complex temp; calls the constructor and thats the output you see. Typically you'd pass x as const reference to avoid the copy, but as you need a copy anyhow you can use x:
complex operator+(complex x)
{
x.real +=real;
x.img +=img;
return x;
}
This will only invoke the compiler generated copy constructor when you call the operator.
There are more subtleties to consider. Using x instead of making a local copy inhibits named return value optimization. Also it is common to implement operator+ in terms of operator+= and operator+ can be a free function then. Note that operator+= can be more efficient, because no copy is needed at all. For more details on operator overloading in general I refer you to What are the basic rules and idioms for operator overloading?
PS: "parametrized constructor" is a term I have seen only in poor misleading tutorials so far. It is not an official term. The relevant term here is default constructor. complex(int x = 0, int y = 0) is a default constructor because it can be called without parameters. It is a good example for why "parametrized constructor" does not convey lots of meaning and is rather misleading.

Understanding and using a copy assignment constructor

I'm trying to understand how the copy assignment constructor works in c++. I've only worked with java so i'm really out of my waters here. I've read and seen that it's a good practice to return a reference but i don't get how i should do that. I wrote this small program to test the concept:
main.cpp:
#include <iostream>
#include "test.h"
using namespace std;
int main() {
Test t1,t2;
t1.setAge(10);
t1.setId('a');
t2.setAge(20);
t2.setId('b');
cout << "T2 (before) : " << t2.getAge() << t2.getID() << "\n";
t2 = t1; // calls assignment operator, same as t2.operator=(t1)
cout << "T2 (assignment operator called) : " << t2.getAge() << t2.getID() << "\n";
Test t3 = t1; // copy constr, same as Test t3(t1)
cout << "T3 (copy constructor using T1) : " << t3.getAge() << t3.getID() << "\n";
return 1;
}
test.h:
class Test {
int age;
char id;
public:
Test(){};
Test(const Test& t); // copy
Test& operator=(const Test& obj); // copy assign
~Test();
void setAge(int a);
void setId(char i);
int getAge() const {return age;};
char getID() const {return id;};
};
test.cpp:
#include "test.h"
void Test::setAge(int a) {
age = a;
}
void Test::setId(char i) {
id = i;
}
Test::Test(const Test& t) {
age = t.getAge();
id = t.getID();
}
Test& Test::operator=(const Test& t) {
}
Test::~Test() {};
I can't seem to understand what i should be putting inside operator=(). I've seen people returning *this but that from what i read is just a reference to the object itself (on the left of the =), right? I then thought about returning a copy of the const Test& t object but then there would be no point to using this constructor right? What do i return and why?
I've read and seen that it's a good practice to return a reference but i don't get how i should do that.
How
Add
return *this;
as the last line in the function.
Test& Test::operator=(const Test& t) {
...
return *this;
}
Why
As to the question of why you should return *this, the answer is that it is idiomatic.
For fundamental types, you can use things like:
int i;
i = 10;
i = someFunction();
You can use them in a chain operation.
int j = i = someFunction();
You can use them in a conditional.
if ( (i = someFunction()) != 0 ) { /* Do something */ }
You can use them in a function call.
foo((i = someFunction());
They work because i = ... evaluates to a reference to i. It's idiomatic to keep that semantic even for user defined types. You should be able to use:
Test a;
Test b;
b = a = someFunctionThatReturnsTest();
if ( (a = omeFunctionThatReturnsTest()).getAge() > 20 ) { /* Do something */ }
But Then
More importantly, you should avoid writing a destructor, a copy constructor, and a copy assignment operator for the posted class. The compiler created implementations will be sufficient for Test.
We return a reference from the assignment operator so we can do some cool tricks like #SomeWittyUsername shows.
The object we want to return a reference to is the one who the operator is being called on, or this. So--like you've heard--you'll want to return *this.
So your assignment operator will probably look like:
Test& Test::operator=(const Test& t) {
age = t.getAge();
id = t.getID();
return *this;
}
You may note that this looks strikingly similar to your copy-constructor. In more complicated classes, the assignment operator will do all the work of the copy-constructor, but in addition it'll have to safely remove any values the class was already storing.
Since this is a pretty simple class, we have nothing we need to safely remove. We can just re-assign both of the members. So this will be almost exactly the same as the copy-constructor.
Which means that we can actually simplify your constructor to just use the operator!
Test::Test(const Test& t) {
*this = t;
}
Again, while this works for your simple class, in production code with more complicated classes, we'll usually want to use initialization lists for our constructors (read here for more):
Test::Test(const Test& t) : age(t.getAge()), id(t.getId()) { }
Returning reference to the original object is needed for support of nested operations.
Consider
a = b = c

Operator overload doubts

I was trying to write some operator overload functions, especially the << operator to use it with a custom class and an std::ofstream object, but I was a bit confused by the syntax used in various examples found online. For example, let's consider the operator<< overload as a non-member function for a simple custom class:
#include <fstream>
class Example {
public:
int first;
int second;
};
// I found this kind of operator on the Internet
std::ofstream& operator<< (std::ofstream& out, Example obj) {
out << obj.first << endl << obj.second;
return out;
}
int main() {
Example a={1,2};
std::ofstream out;
out.open("test");
out << a;
out.close();
}
I don't really get why It should return std::ofstream& to work properly. I tried using the following operator
void operator<< (std::ofstream& out, Example obj) {
out << obj.first << endl << obj.second << endl;
}
and it worked as well. I mean, can't out << obj; be interpreted as operator<< (out , obj); ? Why does it have to return something since I'm passing a reference to the std::ofstream object?
The same doubt arose when I was trying to write an operator= overload as a member function for a custom class, as the simple example that follows
class Custom{
public:
int up;
int down;
Custom& operator= (Custom a) {
up=a.up;
down=a.down;
return *this;
}
};
I used the copy-swap idiom for the assignment operator, so don't mind the operator definition too much, it's just an example. Again, writing
Custom obj1, obj2;
obj1 = obj2;
since I can interpret obj1 = obj2; as obj1.operator=(obj2), why is the return type Custom& required instead of void?
If you want to be able to chain operator<<s together, you have to use a return type (better std::ostream& than std::ofstream&, so you can use it for std::cout and like, too).
out << a << b;
(out << a) << b;
^^^^^^^^^^
lhs has to be a stream
For assignment operator, the reason is essentially the same. C++ syntax allows you to write many expressions requiring the return type, for example this:
Custom obj1, obj2, obj3;
(obj1 = obj2) + obj3 ... // assign obj2 to obj1 and work with that...
Returning a reference allows you to chain the operators, like
std::cout << e1 << e2;
Return a reference instead of void, make it possible to write like
out << obj1 << obj2 << obj3;
For operator=, you can write
obj1=obj2=obj3;
You can write somthing like cout << "First operand" << "Second operand" because first operand returns reference to ostream and second operand works with this reference.
operator= works same way. You can write a = b = c, but also you can put it inside if (a = b) or while (a = b). This can make your code shorter, but is a little dangerous.

How do C++ streams work?

I'd like to know how do stream classes work in C++. When you say:
cout<<"Hello\n";
What does exactly do "<<". I know that cout is an object form iostream that represents the standard output stream oriented to narrow characters (char).
In C "<<" is the bitwise shift operator so it moves bits to the left but in C++ it's and insertion operator. Well, that's all I know, I don't really understand how does this work under the hood.
What I'm asking for is detailed explanation about stream classes in C++, how they are defined and implemented.
Thank you very much for your time and sorry for my English.
Let's create a class that looks like cout (but without as many bells and whistles).
#include <string>
class os_t {
public:
os_t & operator<<(std::string const & s) {
printf("%s", s.c_str());
return *this;
}
};
int main() {
os_t os;
os << "hello\n";
os << "chaining " << "works too." << "\n";
}
Notes:
operator<< is an operator overload just like operator+ or all of the other operators.
Chaining works because we return ourselves: return *this;.
What if you can't change the os_t class because someone else wrote it?
We don't have to use member functions to define this functionality. We can also use free functions. Let's show that as well:
#include <string>
class os_t {
public:
os_t & operator<<(std::string const & s) {
printf("%s", s.c_str());
return *this;
}
};
os_t & operator<<(os_t & os, int x) {
printf("%d", x);
return os;
// We could also have used the class's functionality to do this:
// os << std::to_string(x);
// return os;
}
int main() {
os_t os;
os << "now we can also print integers: " << 3 << "\n";
}
Where else is operator overloading useful?
A great example of how this kind of logic is useful can be found in the GMP library. This library is designed to allow arbitrarily large integers. We do this, by using a custom class. Here's an example of it's use. Note that operator overloading let's us write code that looks almost identical to if we were using the traditional int type.
#include <iostream>
#include <gmpxx.h>
int main() {
mpz_class x("7612058254738945");
mpz_class y("9263591128439081");
x = x + y * y;
y = x << 2;
std::cout << x + y << std::endl;
}
<< is a binary operator in C++, and thus can be overloaded.
You know of the C usage of this operator, where 1 << 3 is a binary operation returning 8. You could think of this as the method int operator<<(int, int) where passing in arguments 1 and 3 returns 8.
Technically, operator<< could do anything. It's just an arbitrary method call.
By convention in C++, the << operator is used for handling streams in addition to being the bit-shift operator. When you perform cout << "Hello!", you are calling a method with prototype ostream & operator<< (ostream & output, char const * stream_me). Note the return value ostream &. That return allows you to call the method multiple times, like std::cout << "Hello World" << "!"; which is calling operator<< twice... once on std::cout and "Hello World", and the second time on the result of that first invocation and "!".
In general, if you were to create a class named class Foo, and you wanted it to be printable, you could define your printing method as ostream & operator<< (ostream & output, Foo const & print_me). Here is a simple example.
#include <iostream>
struct Circle {
float x, y;
float radius;
};
std::ostream & operator<< (std::ostream & output, Circle const & print_me) {
output << "A circle at (" << print_me.x << ", " << print_me.y << ") with radius " << print_me.radius << ".";
}
int main (void) {
Circle my_circle;
my_circle.x = 5;
my_circle.y = 10;
my_circle.radius = 20;
std::cout << my_circle << '\n';
return 0;
}
Operators can be considered functions with syntactic sugar that allow for clean syntax. This is simply a case of operator overloading.

Ostream& operator<< overloading code not working

#include <string>
#include <iostream>
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
class Dummy {
private:
std::string name;
int age;
public:
Dummy(int an_age) {age = an_age;}
bool operator> (Dummy &a) {return age > a.age;}
std::string toString() const {return "The age is " + age;}
};
std::ostream& operator<<(std::ostream& out, const Dummy& d) {return out<< d.toString();}
int main()
{
std::cout << max(3, 7) << std::endl;
std::cout << max(3.0, 7.0) << std::endl;
std::cout << max<int>(3, 7.0) << std::endl;
std::cout << max("hello", "hi") << std::endl;
Dummy d1(10);
Dummy d2(20);
std::cout << max(&d1, &d2) << std::endl;
return 0;
}
I'm pretty new to C++ but not new to programming. I've written the code to play with template and operator overloading in C++.
It took quite a while to make it compile and partially work.
The ostream operator<< is not working properly, only to return the address of the object. I can't figure out the causes.
I managed to make it compile by blind trial and error, so I suspect the code might be broken to some extent. And I may not be aware of what'd be improved.
Your max(&d1,&d2) expression gives you the address, and that is printed out. Your operator overloading is fine.
I assume the line you're talking about is
std::cout << max(&d1, &d2) << std::endl;
The problem is you are passing Dummy * instead of Dummy. That makes max return Dummy *, and since your overloaded operator<< takes (essentially) Dummy, it isn't invoked. If you're trying to pass by reference, you don't need to do anything special on the caller side, just make the function take a reference and the compiler will figure it out.
Don't write your own max, use the standard one instead:
#include <algorithm>
void f() { int a = std::max(8, 4); }
The only difference is that the standard max uses operator < by default, just like everything else in the standard library.
Your toString function does something different from what you think it does. It instead returns the sub string of "The age is " starting at the character number age. For example if age is 3, toString will return " age is ". To convert the integer to string you have to use ostringstream:
std::string toString() const {
std::ostringstream s;
s << "The age is " << age;
return s.str();
}