Immutable class with const attributes - c++

I am trying to define an immutable object FRACTION. Because NOMINATOR and DENOMINATOR defines the object that is created, I make them const.
class Fraction{
const int nominator, denominator;
public:
Fraction(int nominator, int denominator):nominator(nominator), denominator(denominator){}
Fraction(const Fraction& copy): nominator(copy.nominator), denominator(copy.denominator){
// no need to check for a denominator of 0 here since copy must already be a valid Fraction
std::cout << "Copy constructor called\n"; // just to prove it works
}
Fraction Multiply(Fraction frac){
return Fraction(nominator * frac.nominator, denominator * frac.denominator);
}
string toString(){
return "[" + to_string(nominator) + "/" + to_string(denominator) + "]";
}
static void Test();
};
void Fraction::Test(){
cout << endl << "--- TEST: Fraction ----------------------------------------------" << endl;
Fraction fracA = Fraction(2, 1);
Fraction fracB = fracA;
cout << "Before multiplying: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << endl;
Fraction fracC = fracB.Multiply(fracB);
cout << "After multiplying: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << ", fracC=" << fracC.toString() << endl;
//--Update fracB using itself
//fracB = fracB.Krat(fracB);
//cout << "After changing fracB: fracA=" << fracA.toString() << ", fracB=" << fracB.toString() << ", fracC=" << fracC.toString() << endl;
}
Everything seems to work fine except when I try:
fracB = fracB.Multiply(fracB);
then I get an error:
error: object of type 'Fraction' cannot be assigned because its copy assignment operator is implicitly deleted
While
fracC = fracB.Multiply(fracB);
is ok.
I thought the solution would be to deliver custom copy assignment operator, but after several attempts, I always ended up with unchanged fracB. Is there any custom copy assignment operator, that would do the job? Or the whole approach to immutable objects in C++ is wrong here?
I am aware of const-correctness: https://isocpp.org/wiki/faq/const-correctness

C++11 has deprecated the generation of a copy assignment operator if the class has a userdeclared copy constructor.
So use
Fraction& operator=(const Fraction& rhs) = default;
And remove const here
const int nominator, denominator;
How can const be assignable!?
Note that
Fraction fracC= fracB;//copy constructor calling
Fraction fracC= fracB.Multiply(fracB);//copy constructor calling twice
fracC = fracB.Multiply(fracB);//copy = operator calling

You can now define your own copy-assignment operator for classes that contain const member objects without undefined behavior as of c++20.
This was undefined behavior prior to c++ and remains so for complete const objects but not non-const objects with const members.
https://stackoverflow.com/a/71848927/5282154

Related

Does conversion constructor create an object and destroys it if there is no assignment operator in c++?

I have a somehow basic question regarding the conversion constructors and assignment operators. I can't find a similar question but maybe I am searching wrongly.
Anyway.. I had made a class like this
class String
{
private:
// enum { SZ = 80 };
static const int SZ = 80;
char str[SZ]; //array
public:
String() //constructor, no args
{
cout << "Default constructor called, p_str = " << (void*)str << endl;
strcpy(str, "");
}
String( char s[] ) //constructor, one arg
{
cout << "Copy constructor called, p_str = " << (void*)str << endl;
strcpy(str, s);
}
void display() //display string
{
cout << str << endl;
// cout << "str ptr = " << (void*)str << endl;
}
void concat(String s2) //add arg string to
{ //this string
if( strlen(str)+strlen(s2.str) < SZ )
strcat(str, s2.str);
else
cout << "\nString too long";
}
void SetString(char* strToSet)
{
strcpy(str, strToSet);
}
// void operator =(const char* strCpy)
// {
// cout << "Copy assignemnt called..." << endl;
// strcpy(str, strCpy);
// }
~String()
{
cout << "Destructor called..." << endl;
}
void* GetStrPtr()
{
return (void*)str;
}
};
and in the main:
String myStr1 = "Hello Hello";
void* old_str_ptr = myStr1.GetStrPtr();
cout << "old_str_ptr = " <<old_str_ptr << endl;
myStr1 = "hello World!!";
cout << "old_str_ptr = " <<old_str_ptr << endl;
void* new_str_ptr = myStr1.GetStrPtr();
cout << "new_str_ptr = " <<new_str_ptr << endl;
myStr1.display();
cout << (char*)old_str_ptr << endl;
cout << (char*)new_str_ptr << endl;
This is the output I got:
Copy constructor called, p_str = 0x62fdd8
old_str_ptr = 0x62fdd8
Copy constructor called, p_str = 0x62fe28
Destructor called...
old_str_ptr = 0x62fdd8
new_str_ptr = 0x62fdd8
hello World!!
hello World!!
hello World!!
Destructor called...
Can someone explains what happens exactly at this line in main:
myStr1 = "hello World!!"
As I can see that it calls the conversion constructor (as the assignment operator is commented) and the address of "str" array is changed then what I don't understand is that the destructor is called and the address is returned back as seen in the output.
In myStr1 = "hello World!!"; the two types are not compatible, so assigment would normally not be possible. However, the compiler notices that you have an implicit conversion constructor that accepts a pointer-to-char and so it invokes this constructor to create a temporary object from which assignment can happen. This is the sequence of events:
A temporary String object is constructed, invoking String("hello World!"").
The temporary is either copy-assigned (C++ < 11) or move-assigned (C++ >= 11) to myStr1 (you don't overload assignment so you don't observe this step in your output). Whether copy-assigned or move-assigned, the relevant compiler-generated assignment operator performs a simple memberwise value copy in this case since the members can't be moved.
The temporary is destructed.
The location of str does not change simply because it can't: it's an array member of the class, meaning its storage is directly allocated as part of String objects. str evaluated as a pointer points to a region of memory within the String object.
The implicit move-assignment operator simply copies the contents of the source object's str array into the target object's str array.
You see a different value on the second constructor message because this is a different object being constructed, and therefore its str member exists in a different memory location. However, this object is destructed after its value is copied into the myStr1 object.

C++, constructor, destructor

As a general opening question, it can be stated as why some times destructor gets called in the middle of the code?!
what are the different possible scenarios can call a destructor.?
I would like to understand the similar effect in the following code
class complex
{
private:
double re, im;
protected:
public:
complex()
{
cout << "def const " << endl;
}
complex(double r, double i)
{
cout << "parameterized " << endl;
re = r;
im = i;
}
void setdata(double r, double i)
{
re = r;
im = i;
}
void getdata()
{
cout << "enter real" << endl;
cin >> re;
cout << "enter im" << endl;
cin >> im;
}
//there are 3(?) possible variants of addition...check
//1st
void add(complex c1, complex c2)
{
cout << "in add" << endl;
re = c1.re + c2.re;
im = c1.im + c2.im;
}
//2nd
void add(complex c)
{
cout << "in add" << endl;
re += c.re;
im += c.im;
}
//3rd --- (???)
// complex add(complex c1, complex c2)
// {
// complex retc;
// retc.re = c1.re + c2.re;
// retc.im = c1.im + c2.im;
//
// return retc; //this one is very weird
// }
void display()
{
cout << endl << re << " + " << im << "i" << endl;
}
void mul(complex c1, complex c2)
{
cout << "in mul" << endl;
re = c1.re*c2.re - c1.im*c2.im;
im = c1.re*c2.im + c1.re*c2.im;
}
complex mul(complex c)
{
cout << "in mul" << endl;
complex retc;
retc.re = re*c.re - im*c.im;
retc.im = re*c.im + c.re*im;
return retc;
}
~complex()
{
cout << re << " + " << im << "i" << endl;
cout << "dest" << endl;
}
};
int main()
{
complex c1;
c1.getdata();
complex c2(5, 5);
complex c3;
c3.add(c1, c2); //to store the answer of c1 + c2 we need c3 object
c3.display();
//perform c1 + c2 * c3
complex c4;
c4.add(c1, c2.mul(c3)); //can not use mul(c2, c3) for c2 * c3...why???!
cout << "ans1" << endl;
c4.display();
//or we can also do...
c1.add(c2.mul(c3)); //but this will modify c1
cout << "ans2" << endl;
c1.display();
return 0;
}
following is the output
def const
enter real
1
enter im
2
parameterized
def const
in add
1 + 2i
dest
5 + 5i
dest
6 + 7i
def const
in mul
def const
in add
1 + 2i
dest
-5 + 65i
dest
6 + 7i
dest
ans1
-4 + 67i
in mul
def const
in add
-5 + 65i
dest
6 + 7i
dest
ans2
-4 + 67i
-4 + 67i
dest
6 + 7i
dest
5 + 5i
dest
-4 + 67i
dest
any idea why do destructors get called in the middle of nowhere!?
why some times destructor gets called in the middle of the code?
Take these methods for illustration:
void mul(complex c1, complex c2)
{
cout << "in mul" << endl;
re = c1.re*c2.re - c1.im*c2.im;
im = c1.re*c2.im + c1.re*c2.im;
}
void add(complex c1, complex c2)
{
cout << "in add" << endl;
re = c1.re + c2.re;
im = c1.im + c2.im;
}
You invoke it with:
c4.add(c1, c2.mul(c3));
Both add() and mul() are receiving their parameters by value. When the compiler sees a pass-by-value parameter, it creates a new version of the parameter object by means of the copy constructor. All classes have a default copy constructor, which assigns each field member one by one. This new version is used throughout the method, and finally destroyed.
So, when you call mul() in c2 with c3 as parameter, a new complex object is created from c3, and when the end of mul() is reached, it is destroyed. The same happens with the call to add() with c1 and the result of c2.mul(c3).
If you want to avoid this copying (which takes time and resources), then you should change the kind of parameter passing in your functions. Specifically, you can pass them by pointer or by reference. The problem is that this would allow modifications of them inside the function: but in the concrete case of pass-by-reference, you can modify it with const, which gets you the best of both worlds: you get the object efficiently passed without the possibility of modification.
void mul(const complex &c1, const complex &c2)
{
cout << "in mul" << endl;
re = c1.re*c2.re - c1.im*c2.im;
im = c1.re*c2.im + c1.re*c2.im;
}
void add(const complex &c1, const complex &c2)
{
cout << "in add" << endl;
re = c1.re + c2.re;
im = c1.im + c2.im;
}
Taking into account that the methods above do not modify the instance they are being called against, they can be also const themselves.
void mul(const complex &c1, const complex &c2) const
{
cout << "in mul" << endl;
re = c1.re*c2.re - c1.im*c2.im;
im = c1.re*c2.im + c1.re*c2.im;
}
void add(const complex &c1, const complex &c2) const
{
cout << "in add" << endl;
re = c1.re + c2.re;
im = c1.im + c2.im;
}
Also, since these functions do not require an instance of complex, they could also be independent, friend functions, or static methods. Actually, that is worth another answer.
Just as a rule of thumb, when you have just one argument of the same class, as in void mul(complex c2), that's probably a member of the class; you're going to invoke it as c1.mul( c2 ). When you have two arguments, as in void mul(complex c1, complex c2) then it is an independent function (i.e., you'll invoke it as mul( c1, c2 ), that it can be friend if you want it wrapped inside your class or access to the private member of your class. Normally you create these friend functions because you have an operator with an object of another class (or a primitive) at its left. Another issue is that an independent function should better return a new object instead of modifying one of its arguments... as you can see, it gets more and more complex.
Anyway, these are the signatures your methods should sport:
class complex {
public:
// ... more things...
void mul(complex c2);
complex operator*(const complex &c2);
friend complex operator*(int x, const complex &c1);
// ... more things...
};
Also, instead of tying your class to the console with the display() function, better overload the operator <<, and you'll be able to use that functionality with any stream.
Find the complete code in IDEOne.
Hope this helps.
Destructors are called when you delete a pointer explicitly, at the end of the scope for scope variables and at the end of the statement for temporaries.
In your case in
c4.add(c1, c2.mul(c3));
you need to compute c2.mul(c3). It will create a new complex class instance. It will be kept around for add to execute, and it will be destroyed when the call is done, since it's no longer needed.
First of all keep in mind that you should only concern about calling a destructor for an object created dynamically in run time. One straight forward answer for this is, YOU need to verify your codes carefully and find out all the dynamically created objects in your codes and check exactly when it is going to out of scope i.e. the time/area from when that object is never going to use any more, and exactly that point of time you need to call the destructor of that object to clear heap memory. Please keep in mind that destructor is for releasing/cleaning memory to avoid memory leak and better memory management in a process.

Returning wrong instances from functions

I'm having trouble figuring out why my BigInt instances along with the custom vector class (BigIntVector) change when returning out of += into + functions. Looking to my example in main.cpp we have 40 + (-30) which in my BigInt.cpp code means it will turn that into 40-30 (and then print negative symbol at end because 'isPositive' bool is false). Using the debugger, I know for certain that -= returns the correct value of 10 into +=. Therefore in += 'tempThis' contains the vector with 10 in it and is returned to + function. However when it returns to the + function, 'tempThis' in that scope becomes 40? Any reason why?
Thank you.
BigInt.cpp addition
// binary addition
BigInt BigInt::operator+(BigInt const& other) const {
BigInt tempThis = BigInt(*this);
tempThis += other; //tempThis becomes 40 which isn't 10 for 40-30!!!!!!!
return tempThis;
}
// compound addition-assignment operator
BigInt BigInt::operator+=(BigInt const& other) {
if (!other.isPositive) {
BigInt tempThis = BigInt(*this);
tempThis -= other; //tempThis is correctly assigned 10 for 40-30!!!!!!!!
cout << "get element at 0 +=: " << tempThis.bigIntVector->getElementAt(0) << endl;
return tempThis;
}
main.cpp
BigInt num11 = -30;
cout << "num11 (-30): " << num11 << endl;
BigInt num12 = 40;
cout << "num12 (40): " << num12 << endl;
BigInt num13 = num12 + num11;
cout << "num13 (-10): " << num13 << endl;
prints:
num11 (-30): -30
num12 (40): 40
num13 (-10): 40
Your compound addition-assignment operator += should be:
modifying its own value; and
returning a reference to itself, not a copy of its value.
The signature should be:
BigInt& BigInt::operator+=(BigInt const& other)
Within it, you don't want to use a temporary value, or possibly if you have failure conditions that might occur and want to guarantee behaviour, use a temporary variable and then overwrite your 'this' object if it succeeds.
Have a look at this page: http://courses.cms.caltech.edu/cs11/material/cpp/donnie/cpp-ops.html
for more information. Search within the page for "Compound Assignment Operators" if you don't want to read the whole thing.
You're missing the overload for the assignment operator and you need to return references to BigInt i.e.
BigInt& BigInt::operator+(BigInt const& other) const

C++: Parent object change their id attribute without method call

As I specified in the title, this is a problem that puzzled me enormously, and still I can not find a fix for it.
I created a Point class which has three private attributes (_id, _x and _y) with related public get and set methods.
For example, I created three points (G, H, R), where the point G is a "parent". Point H is the prototype of the point G (a clone) and point R will be the outcome of the meeting points (G+H) (addition of the points).
Points sample:
Point G ("pG", 3.5f, 6.5f);
Point H (G.prototype ()); H.setId ("pH");
Point R;
R = G + H;
The program works correctly, but unfortunately after the operation {R=G+H}, _id attribute of point G becomes the clone attribute of point R, I do not understand why this is happening because the evaluation is done from right to left (H+, +G, =R).
The question is why G point attribute changes itself?
Code where the problem occurs:
#include <iostream>
#include <string>
using namespace std;
class Point
{
private:
string _id;
double _x;
double _y;
public:
Point(string id="undefined", double x = 0.00f, double y = 0.00f)
:_id(id), _x(x), _y(y){}
~Point(){}
public:
// Getters methods
string getId() const { return _id;}
double getX() const { return _x; }
double getY() const { return _y; }
void setId(string id) { _id = id; }
// Setters methods
void setX(double n = 0.00f) { _x = n; }
void setY(double n = 0.00f) { _y = n; }
Point prototype() { return Point(_id, _x, _y); }
Point operator+ (const Point& p)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
_id.append("+").append(p._id),
_x+p._x,
_y+p._y
);
}
Point operator= (const Point& p)
{
cout << "Operator = called, returning a new object Point(p._id, p._x, p._y)" << endl;
_id=p._id;
_x=p._x;
_y=p._y;
return *this;
}
};
int main()
{
Point G("G",10.0f, 10.0f);
Point H(G.prototype()); H.setId("H");
Point R;
R = G + H;
cout << "object Point {id: " << G.getId() << ", x: " << G.getX() << ", y: " << G.getY() << "}" << endl;
cout << "object Point {id: " << H.getId() << ", x: " << H.getX() << ", y: " << H.getY() << "}" << endl;
cout << "object Point {id: " << R.getId() << ", x: " << R.getX() << ", y: " << R.getY() << "}" << endl;
return 0;
}
Thanks in advance!
Point operator+ (const Point& p)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
_id.append("+").append(p._id), // HERE
_x+p._x,
_y+p._y
);
}
In this line, you modify _id property of this object. As a rule of thumb, binary + operator should be a const member or - IMO cleaner - a static method taking two const reference arguments.
BTW, you can add strings in C++ by just applying + operator to them (_id + "+" + p._id).
If you implement your binary arithmetic operators as members, you should consider making the operator a const member. This would catch the obvious modification in your implementation (the implementation is made out of line to avoid including unrelated code):
Point Point::operator+ (const Point& p) const
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
_id.append("+").append(p._id),
_x+p._x,
_y+p._y
);
}
The operation _id.append("+") actually operates on this->_id, i.e., the _id member of the left hand operand. Since you member operator isn't const, the compiler lets you do the modification. This is probably not what you intended to do. You probably rather wanted to write:
Point Point::operator+ (const Point& p) const
{
cout << "Operator + called, returning a new object\n";
return Point
(
_id + "+" + p._id,
_x+p._x,
_y+p._y
);
}
... which creates a suitable temporary string with the desired value (I also replaced the excessive use of std::endl).
This line in the operator+ overload:
_id.append("+").append(p._id),
You have to be very careful here, you are currently in an object (in your example this would be the G object). append actually alters the local string, so your code is appending + then p._id then deep copying that to the returned value.
A quick fix is to change this to a temporary that holds what you need:
_id + "+" + p._id
To avoid problems like this in the future, you should declare operator overloads as a friend method so it's extremely clear what you're operating on.
friend Point operator+ (const Point& lhs, const Point& rhs)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
lhs._id.append("+").append(rhs._id), //COMPILER ERROR! We're modifying the const "lhs"!
lhs._x+rhs._x,
lhs._y+rhs._y
);
}
So let's change this to:
friend Point operator+ (const Point& lhs, const Point& rhs)
{
cout << "Operator + called, returning a new object" << endl;
return Point
(
lhs._id + "+" + rhs._id, // no longer modifying lhs -- all is well!
lhs._x+rhs._x,
lhs._y+rhs._y
);
}

Compiler behavior regarding class objects I don't understand

I'm a noob and still learning the c++ language. The thing is, doing an exercise from a book, I've come across a compiler behavior I don't understand.
The header file.
// stock10.h -- Stock class declaration with constructors, destructor added
#ifndef STOCK10_H_
#define STOCK10_H_
#include <string>
class Stock
{
private:
std::string company;
long shares;
double share_val;
double total_val;
void set_tot() { total_val = shares * share_val; }
public:
// two constructors
Stock(); // default constructor
Stock(const std::string & co, long n = 0, double pr = 0.0);
~Stock(); // noisy destructor
void buy(long num, double price);
void sell(long num, double price);
void update(double price);
void show();
};
#endif
The class implementation.
// stock10.cpp -- Stock class with constructors, destructor added
#include <iostream>
#include "stock10.h"
// constructors (verbose versions)
Stock::Stock() // default constructor
{
std::cout << "Default constructor called\n";
company = "no name";
shares = 0;
share_val = 0.0;
total_val = 0.0;
}
Stock::Stock(const std::string & co, long n, double pr)
{
std::cout << "Constructor using " << co << " called\n";
company = co;
if (n < 0)
{
std::cout << "Number of shares can’t be negative; "
<< company << " shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
// class destructor
Stock::~Stock() // verbose class destructor
{
std::cout << "Bye, " << company << "!\n";
}
// other methods
void Stock::buy(long num, double price)
{
if (num < 0)
{
std::cout << "Number of shares purchased can’t be negative. "
<< "Transaction is aborted.\n";
}
else
{
shares += num;
share_val = price;
set_tot();
}
}
void Stock::sell(long num, double price)
{
using std::cout;
if (num < 0)
{
cout << "Number of shares sold can’t be negative. "
<< "Transaction is aborted.\n";
}
else if (num > shares)
{
cout << "You can’t sell more than you have! "
<< "Transaction is aborted.\n";
}
else
{
shares -= num;
share_val = price;
set_tot();
}
}
void Stock::update(double price)
{
share_val = price;
set_tot();
}
void Stock::show()
{
using std::cout;
using std::ios_base;
// set format to #.###
ios_base::fmtflags orig =
cout.setf(ios_base::fixed, ios_base::floatfield);
std::streamsize prec = cout.precision(3);
cout << "Company: " << company
<< " Shares: " << shares << '\n';
cout << " Share Price: $" << share_val;
// set format to #.##
cout.precision(2);
cout << " Total Worth: $" << total_val << '\n';
// restore original format
cout.setf(orig, ios_base::floatfield);
cout.precision(prec);
}
The main file.
// usestok1.cpp -- using the Stock class
// compile with stock10.cpp
#include <iostream>
#include "stock10.h"
int main()
{
{
using std::cout;
cout << "Using (non default) constructors to create new objects\n";
Stock stock1("NanoSmart", 12, 20.0); // syntax 1
stock1.show();
Stock stock2 = Stock ("Boffo Objects", 2, 2.0); // syntax 2
stock2.show();
cout << "Assigning stock1 to stock2:\n";
stock2 = stock1;
cout << "Listing stock1 and stock2:\n";
stock1.show();
stock2.show();
cout << "Using a constructor to reset an object\n";
stock1 = Stock("Nifty Foods", 10, 50.0); // temp object
cout << "Revised stock1:\n";
stock1.show();
cout << "Done\n";
}
std::cin.get();
return 0;
}
As you may have guessed, Stock is the class and I've created non-default constructor and destructor to display messages to see when they "act".
Here's the output from program execution:
Using (non default) constructors to create new objects
Constructor using NanoSmart called
Company: NanoSmart Shares: 12
Share Price: $20.000 Total Worth: $240.00
Constructor using Boffo Objects called
Company: Boffo Objects Shares: 2
Share Price: $2.000 Total Worth: $4.00
Assigning stock1 to stock2:
Listing stock1 and stock2:
Company NanoSmart Shares: 12
Share Price: $20.000 Total Worth: $240.00
Company NanoSmart Shares: 12
Share Price: $20.000 Total Worth: $240.00
Using a constructor to reset an object
Constructor using Nifty Foods called
Bye, NanoSmart! // Why? Shouldn't it be Bye, Nifty Foods?
Revised stock1:
Company: Nifty Foods Shares: 10
Share Price: $50.000 Total Worth: $500.00
Done
Bye, NanoSmart!
Bye, Nifty Foods!
In this specific line:
stock1 = Stock("Nifty Foods", 10, 50.0); // temp object
Shouldn't the compiler:
1. Create a temporary object with the constructor
2. Assign that object to the stock1 object
3. Destroy the temporary object
Shouldn't the message say Nifty Foods instead of NanoSmart?
I don't get it. Any help?
You don't have a assignment operator defined, so if you are using a C++11 compiler, it probably uses the move assignment operator, which swaps the objects and then deletes the new contents of the temp object, which used to be in stock1.
At least, that is the observed behavior. ecatmur is correct, however, that your class should not have received an implicit move assignment operator. That might of course be a compiler bug.
You haven't written a copy assignment operator Stock::operator=(const Stock &) or move assignment operator Stock::operator=(Stock &&), so your assignment stock1 = Stock("Nifty Foods", 10, 50.0); will invoke an implicitly defined copy/move assignment operator:
c++11
12.8 Copying and moving class objects [class.copy]
18 - If the class definition does not explicitly declare a copy assignment operator, one is declared implicitly. If
the class definition declares a move constructor or move assignment operator, the implicitly declared copy
assignment operator is defined as deleted; otherwise, it is defined as defaulted (8.4).
28 - The implicitly-defined copy/move assignment operator for a non-union class X performs memberwise copy-
/move assignment of its subobjects.
Because your class has a user-defined destructor, the move assignment operator will not be implicitly defined (12.8:20), so the implicitly defined copy assignment operator will be called:
20 - If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly
declared as defaulted if and only if [...]
X does not have a user-declared destructor
Thus the Stock("Nifty Foods", 10, 50.0) will be memberwise copied into stock1 and then destructed; so the message displayed will be "Bye, Nifty Foods!".
Here's a SSCCE:
#include <iostream>
#include <string>
struct S {
std::string s;
S(const std::string &s): s(s) { std::cout << "S(" << s << ")\n"; }
~S() { std::cout << "~S(" << s << ")\n"; }
};
int main() {
S a("a");
a = S("b");
}
Output:
S(a)
S(b)
~S(b)
~S(b)
I don't see anything immediately wrong with your code, but you should probably implement a copy-constructor and assignment operator anyway, just to be sure that copying is performed correctly.
something like this:
class Stock
{
// ...
public:
// ...
Stock(const Stock &other)
: company(other.company), shares(other.shares),
share_val(other.share_val), total_val(other.total_val)
{ }
Stock &operator=(const Stock &other)
{
company = other.company;
shares = other.shares;
share_val = other.share_val;
total_val = other.total_val;
return *this;
}
// ...
};
For more information about copy-constructors, see e.g. this Wikipedia article. For the assignment operator, see e.g. this article.