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.
Related
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.
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
This question already has answers here:
Why should the assignment operator return a reference to the object?
(4 answers)
Closed 6 years ago.
Some of the assignment overloading operator examples I see online look like this:
#include <iostream>
using namespace std;
class Distance {
private:
int feet; // 0 to infinite
int inches; // 0 to 12
public:
// required constructors
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
void operator = (const Distance &D ) {
cout << "assigning..." << endl;
feet = D.feet;
inches = D.inches;
}
// method to display distance
void displayDistance() {
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main() {
Distance D1(11, 10), D2(5, 11);
cout << "First Distance : ";
D1.displayDistance();
cout << "Second Distance :";
D2.displayDistance();
// use assignment operator
D1 = D2;
cout << "First Distance :";
D1.displayDistance();
return 0;
}
They return void from the overloaded function. This makes sense to me if D1 is the object being called.
Other examples return a reference to a class object.
#include <iostream>
using namespace std;
class Distance {
private:
int feet; // 0 to infinite
int inches; // 0 to 12
public:
// required constructors
Distance(){
feet = 0;
inches = 0;
}
Distance(int f, int i){
feet = f;
inches = i;
}
Distance& operator = (const Distance &D ) {
cout << "assigning..." << endl;
feet = D.feet;
inches = D.inches;
return *this;
}
// method to display distance
void displayDistance() {
cout << "F: " << feet << " I:" << inches << endl;
}
};
int main() {
Distance D1(11, 10), D2(5, 11);
cout << "First Distance : ";
D1.displayDistance();
cout << "Second Distance :";
D2.displayDistance();
// use assignment operator
D1 = D2;
cout << "First Distance :";
D1.displayDistance();
return 0;
}
This does not make sense to me (when taking the first example into consideration). If in the first example D1 = D2; invokes something like D1.=(D2);, why would the second example work in that case? Is it something like D1 = D1.=(D2);? And does it make any difference at the end of the day?
Although C++ language lets you overload assignment operator with any return type, including void, you should strongly consider following a widespread convention of returning a reference to the assignee from the operator.
The rationale for it is that
A = B;
will work no matter what the assignment returns, while
A = B = C;
which is a perfect chain of assignments will break, unless B = C returns something assignment-compatible to A (which is usually an object of the same type as A).
Another problem is in situations when you must compare the object as part of a larger expression, for example
mytype obj;
while ((obj = read_obj(cin)) != END_OBJ) {
...
}
Hence, the biggest drawback to returning void is inability to chain assignments and use them in places where void is not allowed.
As a convention, assignment operator usually returns reference (to *this); which makes it possible to chain the assignment, just like the behavior of those built-in types. e.g.
Distance D1, D2, D3;
D1 = D2 = D3;
For D1 = D2;, it's equivalent with D1.operator=(D2);. It doesn't change for the 2nd case, the returned value is just discarded. For D1 = D2 = D3;, it's equivalent with D1.operator=(D2.operator=(D3));. Note the returned value (i.e. reference to D2) is used as the argument for the assignment operator called on D1.
I have a question regarding the following code, which crashes. I am creating a local variable in testfunction() and then pushing it (variable "y") into a list. This variable has a member pointer "b" of object type Ball. As I understand, this local variable "y" is on the stack, so its' destructor will be called after testfunction() is completed. Also, as I understand, a vector "copies" an object into its' list. From what I've learned, it is best practice to delete a pointer in the destructor if one exists in its' class. So, there is "delete b" in the destructor of Example.
The issue that I am having is that the object y.b is being destroyed at the completion of testfunction(). In main(), I am able to see the value of "name" and the address of "b", but the object "b" has already been deleted. I would like to avoid this.
I think there is an issue with the design of the code/use of pointers vs references, etc. Please guide me in the right direction, I am an idiot!
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Ball
{
public:
int a;
Ball()
{
a = 0;
}
~Ball()
{
cout << "destroyed Ball()" << endl;
}
};
class Example
{
public:
string name;
Ball* b;
Example()
{
name = "";
b = NULL;
}
~Example()
{
cout << "destroying Example()" << endl;
delete b;
}
};
void testfunction(vector<Example>& list)
{
cout << "entered testfunction1()" << endl;
Example y;
y.name = "myName";
y.b = new Ball();
y.b->a = 5;
cout << "y.b->a = " << y.b->a << endl;
list.push_back(y);
cout << "exit testfunction1()" << endl;
}
void testfunction2()
{
cout << "entered testfunction2()" << endl;
Example* y = new Example();
cout << "exit testfunction2()" << endl;
}
int main() {
vector<Example> list;
testfunction(list);
//testfunction2();
if(list[0].b == NULL)
cout << "b is null" << endl;
else
cout << "b is not null" << endl;
cout << list[0].name << endl;
cout << list[0].b << endl;
cout << "list[0].b->a = " << list[0].b->a << endl;
return 0;
}
Since class Example has a pointer member and it tries to own a dynamically allocated resource, it needs non-default copy operations, in other words, it needs user-defined copy constructor and assignment operator.
Inside testfunction, when you copy y into vector, both local y and y copied to the vector point to very same Ball object. The local y is destroyed at the end of the function and Ball is deleted. However, that deleted Ball still pointed by the y in vector
void testfunction(vector<Example>& list)
{
// ...
Example y;
y.name = "myName";
y.b = new Ball();
y.b->a = 5;
list.push_back(y);
// ...
} // <-- destructor for Example y is called and y.b is deleted
Define a copy constructor and an assignement operator for your class Example.
These shall copy properly your object (creating a duplicated Ball object) when pushed back on the vector.
Example(const Example& a)
{
name = a.name; // attention no dynamic allocation
cout << "copy" <<endl;
if (a.b) {
b = new Ball(*a.b); // create a new duplicated Ball
}
else b = NULL;
}
The problem in your example is that the default copy constructor is called when you pushback the object. It copies memberwise and so the pointer to Ball is copied, not the object pointed to.
Yet another alternative could be to replace your Ball* with shared_ptr<Ball> (and accordingly, new Ball with make_shared<Ball>() and the delete b of the object with a b.reset()). The principle is that this smart pointer keeps track of the number of time the object pointed to is used, so that it will not delete it twice, but only when its no longer used anywhere.
Here is the code first, it comes from 'Ruminations on C++' chapter 10
// TestCode.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <iostream>
#include <string>
#include <conio.h>
using namespace std;
class P_Node
{
friend class Picture;
protected:
P_Node() : use(1)
{
}
virtual ~P_Node()
{
}
private:
int use;
};
class Picture
{
friend Picture frame(const Picture&);
public:
Picture() : p(new P_Node)
{
cout << "Constructor\t" << "Picture::Picture()" << "\tcalled" << endl;
cout << "Picture p count\t" << p->use << endl;
}
Picture(const Picture& orig) : p(orig.p)
{
cout << "Copy Constructor\t" << "Picture::Picture(const Picture&)" << "\tcalled" << endl;
cout << "Picture p count\t" << p->use << endl;
orig.p->use++;
}
~Picture()
{
cout << "Destructor\t" << "Picture::~Picture()" << "\tcalled" << endl;
cout << "Picture p count before decrease\t" << p->use << endl;
if(--p->use == 0)
{
cout << "Picture p count after decrease\t" << p->use << endl;
cout << "Deleted" << endl;
delete p;
}
}
Picture& operator=(const Picture& orig)
{
cout << "operator=\t" << "Picture& Picture::operator=(const Picture& orig)" << "\tcalled" << endl;
cout << "Picture p count before decrease\t" << p->use << endl;
orig.p->use++;
if(--p->use == 0)
{
cout << "Picture p count after decrease\t" << p->use << endl;
delete p;
}
p = orig.p;
return *this;
}
private:
Picture(P_Node* p_node) : p(p_node)
{
// Why not p_node->use++?
cout << "Picture::Picture(P_Node* p_node)\tcalled" << endl;
}
P_Node *p;
};
class Frame_Pic : public P_Node
{
friend Picture frame(const Picture&);
private:
Frame_Pic(const Picture& pic) : p(pic)
{
cout << "Frame_Pic::Frame_Pic(const Picture& orig)" << "\tcalled" << endl;
}
Picture p;
};
Picture frame(const Picture& pic)
{
return new Frame_Pic(pic);
}
int main(int argc, char* argv[])
{
Picture my_pic;
frame(my_pic);
return 0;
}
The result is:
Constructor Picture::Picture() called
Picture p count 1
Copy Constructor Picture::Picture(const Picture&) called
Picture p count 1
Frame_Pic::Frame_Pic(const Picture& orig) called
Picture::Picture(P_Node* p_node) called
Destructor Picture::~Picture() called
Picture p count before decrease 1
Picture p count after decrease 0
Deleted
Destructor Picture::~Picture() called
Picture p count before decrease 2
Destructor Picture::~Picture() called
Picture p count before decrease 1
Picture p count after decrease 0
Deleted
I have two questions about this code:
Why is the copy constructor called before Frame_Pic's Constructor? In my mind, the copy constructor is called because frame(my_pic) is returning a Picture by value. But that should be called after Frame_Pic's Constructor.
In Picture::Picture(P_Node* p_node), why not increase the use count? isn't this creating a new Picture?
Thanks for any help.
I'm using VC6 under Windows XP.
1, Why is the Copy Constructor called before Frame_Pic's Constructor?
Because the p member is being copy-constructed in the initialization list of Frame_pic's constructor. The initialization list runs before the constructor's body is entered.
In my mind, the Copy Constructor is called because frame(my_pic) is returning a Picture by value. But that should be called after Frame_Pic's Constructor.
frame() is declared to return a Picture instance by value, but it is coded to return a Frame_pic* instead. Frame_pic derives from P_node, and Picture has a constructor that accepts a P_node*, and that constructor is accessible to frame() so the compiler allows it.
2, In Picture::Picture(P_Node* p_node), why not increase the use count? isn't this creating a new Picture?
The use count is on P_node, not Picture. The Picture that frame() returns owns the Frame_pic that frame() creates, whose use count member is already 1 by the Frame_pic constructor. That is why that Picture constructor does not increment the use count - it is already at the correct value.
The Frame_pic contains its own Picture that is copy-constructed from another Picture, so that Picture constructor needs to increment the use count of the original Picture.
The copy constructor is invoked by Frame_Pic's constructor (it's invoked by the initializer : p(pic)). However Frame_Pic's constructor doesn't print until after all initializers have been run.
Because the intended use of the constructor is not documented, it's hard to say. It may be a bug, or it may be 'attach semantics' - that is, it may be considering the case where you take manual control of the P_Node*, then later give it back. However attach semantics is unlikely, as there's no corresponding detach mechanism to return the pointer and clear it without decrementing the refcount. So, most likely a bug.
Note that while this sort of manual reference counting can be good as a learning exercise, modern C++ code generally uses smart pointers (eg, std::shared_ptr or boost::shared_ptr, invasive_ptr, etc) to automate the process.
Frame_Pic(const Picture& pic) : p(pic)
{
cout << "Frame_Pic::Frame_Pic(const Picture& orig)" << "\tcalled" << endl;
}
You initializing 'p' using its copy constructor 'p(pic)', hence it gets called in the order you see