I wrote a little class to learn the different constructor calls
#include <iostream>
#include <cstdlib>
#include <cstring>
class String {
private:
char *str;
public:
explicit String(const char *p);
String(String&& StringObject);
String(String& stringObject);
~String();
friend std::ostream& operator<<(std::ostream& os, const String& s);
friend String operator+(const String& s1, const String& s2);
};
String::String(const char *p)
{
size_t l = strlen(p) + 1;
str = (char *)malloc(sizeof(char) * l);
if (str)
strcpy(str, p);
std::cout << "constructor call" << std::endl;
}
String::String(String& stringObject)
{
str = (char *)malloc(sizeof(char) * (strlen(stringObject.str) + 1));
strcpy(str, stringObject.str);
std::cout << "copy constructor call" << std::endl;
}
String::~String()
{
free(str);
}
String::String(String&& stringObject)
{
this->str = stringObject.str;
stringObject.str = nullptr;
std::cout << "move constructor call" << std::endl;
}
std::ostream& operator<<(std::ostream& os, const String& s)
{
return os << s.str;
}
String operator+(const String& s1, const String& s2)
{
size_t sl1 = strlen(s1.str);
size_t sl2 = strlen(s2.str);
char str[sl1 + sl2 + 1];
strcpy(str, s1.str);
strcpy(str+sl1, s2.str);
return String{str};
}
String doNothing(String obj)
{
std::cout << "output in function: " << obj << std::endl;
return obj;
}
int main()
{
String s1("text");
String s2("and more text");
std::cout << "output: " << s1 << std::endl;
String s3 = doNothing(s1+ String{" "} + s2);
String s4 = doNothing(s1);
String s5 = s1 + s4;
}
The output is
constructor call
constructor call
output: text
constructor call
constructor call
constructor call
output in function: text and more text
move constructor call
copy constructor call
output in function: text
move constructor call
constructor call
I think the constructor calls on line 4 to 6 come from the method call
String s3 = doNothing(s1+ String{" "} + s2);
Why does the method call not cause a call to the copy constructor like the second method call?
String s4 = doNothing(s1);
Maybe because s1 is a lvalue?
Can the move constructor only be called when a function returns an object or also when a reference or pointer to an object is returned?
I'd suppose that you start line counting at 1 ;-)
The case with a complex expression
We would expect the following statement to construct in principe a temporary String for String{" "}, and then a temporary string for each of the two +. Since the parameter obj is to be constructed from a temporary object, the move constructor could be used:
String s3 = doNothing(s1+ String{" "} + s2);
The idea, of the move constructor is to be able to take advantage of the fact that the original object is disposable.
The return value of the function is also a temporary value and this one is used to construct s3 with the move constructor.
But shouldn't we then have 3 constructors and two move constructors? No, because there are copy elision rules. These cause the compiler to avoid unnecessary a copies/moves, and construct directly into the target. This happens here for the parameter.
The case with the lvalue
The next statement constructs the parameter obj with the copy constructor of an lvalue s1:
String s4 = doNothing(s1);
This is less surprising. The value is in principle copied to a temporary return value, which is then used to move-construct s4 from a temporary.
But again, copy elision simplifies this to a copy constructor and a move.
Analysing what happens
You could analyse more in detail what happens, by displaying the address of the object. This is very helpful to understand what happens on what object:
class String {...};
String::String(const char *p)
{
size_t l = strlen(p) + 1;
str = new char[l];
if (str)
strcpy(str, p);
std::cout << "constructor call:" << this<<"="<<str<< std::endl;
}
String::String(const String& stringObject)
{
str = new char[ strlen(stringObject.str) + 1];
strcpy(str, stringObject.str);
std::cout << "copy constructor call:" <<this<<"="<<str<< std::endl;
}
String::~String()
{
delete[] str;
}
String::String(String&& stringObject)
{
this->str = stringObject.str;
stringObject.str = nullptr;
std::cout << "move constructor call:"<<this <<"="<<str<< std::endl;
}
String operator+(const String& s1, const String& s2)
{
size_t sl1 = strlen(s1.str);
size_t sl2 = strlen(s2.str);
char str[sl1 + sl2 + 1];
strcpy(str, s1.str);
strcpy(str+sl1, s2.str);
return String{str};
}
String doNothing(String obj)
{
std::cout << "output in function: " <<&obj<<":"<< obj << std::endl;
return obj;
}
Online demo
Unrelated advice
I highly recommend that you get rid of malloc() and free() in C++ so I used here new[] and delete[] (or new and delete if it's not about arrays). In a second step you could alsoe get rid of the strcpy()
Related
I am writing my own String.h for learning purpose.
I am trying to implementing the following :
String s = "Hello World"; //This works
s += " Hi"; // Should be "Hello World Hi"
The following is my operator overload:
String& operator+=(const char* rhs)
{
size_t length = m_length + strlen(rhs);
char* string = new char[length + 1];
strncpy_s(string, length + 1, m_string, m_length);
String s = string;
std::cout << "Concat String : " << s << "\n"; //This prints correctly
return s;
}
And my constructor :
String(const char* string)
{
if (!string)
{
m_length = 0;
m_string = new char[0];
}
else
{
m_length = strlen(string);
m_string = new char[m_length + 1];
strncpy_s(m_string, m_length+1,string,m_length);
}
}
This is my << overload :
std::ostream& operator<<(std::ostream& out, String& string)
{
out << string.m_string;
return out;
}
The issue :
When I print cout << s; in the operator+= it is printing correctly but in the main function where I am calling it :
String s = "Hello World";
s += " Hi";
std::cout << s;
It is coming as "Hello World".
Please help. I am still on my learning path.
Thanks in advance.
Am wondering why the copy assignment operator is not been called and yet the assignment seems to happen? This is my code:
#include <cstring>
#include <iostream>
class String{
public:
String(){ std::cout << "Empty Ctor" << '\n'; }
String(const char* s):data{(char*)s}{ std::cout << "Args Ctor"; }
Strin& operator=(const String&);
char* showData(){ return *data; }
private:
char* data;
};
String& String::operator=(const String& s){
std::cout << "Copy Assignment" << '\n'; // <- This is NEVER printed
delete [] data;
data = new char[strlen(s.data)+1];
strcpy(data, s.data);
return *this;
}
int main(){
String* s1 = new String("Hello");
String* s2 = new String("World");
s2 = s1; // This should call the overloaded copy assignment operator!?
std::cout << s2->showData() << '\n'; // prints "Hello"
return 0;
}
The cout in the assignment operator is not been printed
Updating my code - based on earlier suggestions. This works as expected
#include <cstring>
#include <iostream>
class String{
public:
String():data{nullptr}{ std::cout << "Empty ctor" <<'\n' ;}
String(const char* s)
{
std::cout<< "Const Args ctor" <<'\n' ;
data = new char[strlen(s)+1];
strcpy(data, (char *)s);
}
String(const String& other){
std::cout << "Copy Ctor" << '\n';
*this = other;
}
String& operator=(const String& s){
std::cout << "Assignment" << '\n';
if (&s != this){
delete [] data;
data = new char[strlen(s.data)+1];
strcpy(data, s.data);
}
return *this;
}
~String(){
std::cout << "Dtor" << '\n';
delete [] data;
}
char* showData(){ return data; }
private:
char* data;
};
int main()
{
std::cout << "Main" << '\n';
String a("A: Hello");
std::cout << a.showData() <<'\n';
String b("B: World");
std::cout << b.showData() <<'\n';
b = a;
std::cout << b.showData() <<'\n';
return 0;
}
s2 = s1; is just assignment between pointers, it won't call the copy assignment operator, just makes s2 pointing to the same object pointed by s1.
You might want to try with *s2 = *s1;, which uses the copy assignment operator of String. But you don't need the raw pointer indeed, you can just
String s1("Hello");
String s2("World");
s2 = s1; // This would call the overloaded copy assignment operator
std::cout << s2.showData() << '\n'; // prints "Hello"
"s2 = s1; // This should call the overloaded copy assignment operator!?"
No. It should not.
You are just assigning the value of one pointer variable to another. No copy assignment of your objects the pointers point to is happening.
This would do as you expect:
String s1("Hello");
String s2("World");
s1 = s2;
As would
*s1 = *s2;
in your original code.
Ok, lets start with this, pointer is not a string, it is a pointer (that points to a string).
So this:
String* s1 = new String ("BLE");
is creating a string somewhere in memory, and the point that will point to it. So a representation of it is:
+--------+
s -> | 0x005 |----+
+--------+ \
\
\
\
\
+---+---+---+---+---+---+---+
| | | | | B | L | E |
+---+---+---+---+---+---+---+
0x 1 2 3 4 5 6 7
This:
String* s2;
s2 = s1;
makes s2 pointing to same address (holding the same value at assign location for the pointer):
+--------+
s1-> | 0x005 |+
+--------+ \
\
+--------+ \
s2-> | 0x005 |----+
+--------+ \
\
\
+---+---+---+---+---+---+---+
| | | | | B | L | E |
+---+---+---+---+---+---+---+
0x 1 2 3 4 5 6 7
Operator = in String is for type String not for the pointer, so if you have something like this:
String s1 = "BLA";
String s2 = s1;
This creates brand new object and copies data over to that new object.
I suggest you read Programming Principles and Practice Using C++ chapter 18.5.
I also believe you have a lot of errors in your code. You cannot do what you are doing.
Here is sample of your code which actually uses copy assignment:
#include <cstring>
#include <iostream>
class String{
public:
String() : data {new char[1]} { std::cout << "Empty Ctor\n" << '\n'; }
String(const char* s):data{new char[strlen(s)]}{ strcpy (data, s); std::cout << "Args Ctor\n"; }
String& operator=(const String&);
char* showData(){ return data; }
private:
char* data;
};
String& String::operator=(const String& s){
std::cout << "Copy Assignment" << '\n'; // <- This is NEVER printed
delete[] data;
data = new char[strlen(s.data)+1];
strcpy(data, s.data);
return *this;
}
int main(){
String s1 ("Hello");
String s2 ("World");
s2 = s1; // This should call the overloaded copy assignment operator!?
std::cout << s2.showData() << '\n'; // prints "Hello"
return 0;
}
or as other suggested:
#include <cstring>
#include <iostream>
class String{
public:
String() : data {new char[1]} { std::cout << "Empty Ctor\n" << '\n'; }
String(const char* s):data{new char[strlen(s)]}{ strcpy (data, s); std::cout << "Args Ctor\n"; }
String& operator=(const String&);
char* showData(){ return data; }
private:
char* data;
};
String& String::operator=(const String& s){
std::cout << "Copy Assignment" << '\n'; // <- This is NEVER printed
delete[] data;
data = new char[strlen(s.data)+1];
strcpy(data, s.data);
return *this;
}
int main(){
String* s1 = new String("Hello");
String* s2 = new String("World");
*s2 = *s1; // This should call the overloaded copy assignment operator!?
std::cout << s2->showData() << '\n'; // prints "Hello"
return 0;
}
This is reason why you should learn C and pointers before you start messing around with pointers in C++.
Because you are assigning a pointer to another pointer. Do this:
*s2 = *s1;
So I tried operator overloading of [] and it just doesn't work.
I have created this class:
class String
{
private:
char* str;
public:
String(char* str) // constructor
{
this->str = str;
}
char* Val() // returns value of this.str
{
return this->str;
}
char & operator [](int index) { return this->str[index]; }
};
and I tried to use it like so
String* str = new String("example");
cout << str[2] << endl;
the expected result was a print of the letter 'a' but it won't work..
it does work though when I create an object like so:
String str("example");
cout << str[2] << endl;
any suggestions?
String* str = new String("example");
cout << str[2] << endl;
here str is a pointer, so str[2] is not calling your operator, but accesses the third object in memory from the address str which doesn't exist, thus you have Undefined Behavior.
What you need is:
cout << (*str)[2] << endl;
str is a pointer to a String, so in str[2] the compiler is treating str as an array.
You need to access the object - so deference the pointer. i.e. (*str)[2]
In the first example str is a pointer to a String object.
You need to dereference it first, and then call the operator.
This code doesn't compile (please ignore passing by value in operator+, I could replace it by reference & but it still doesn't solve the issue)
I expect in main function:
String s3 = s + s2; // COMPILATION ERROR
to be compiled ok (because I declare copy constructor) but it gives error ("no matching constructor")
#include <iostream>
#include <string>
class String {
public:
String()
{
std::cout << "Constructor " << this << std::endl;
data = new char[100];
};
String(char * str) : String()
{
std::cout << "Char * Constructor " << this << std::endl;
strcpy(this->data,str);
};
String(String & rhs) : String() {
std::cout << "Copy Constructor " << this << std::endl;
strcpy(data, rhs.data);
};
void print() {
printf("%s\n",data);
}
~String() {
std::cout << "Destructor " << this << std::endl;
if (data) {
delete data;
data = nullptr;
}
};
friend String operator+(String s1, String s2);
private:
char * data;
};
String operator+(String s1, String s2)
{
String temp;
delete [] temp.data;
temp.data =
new char[strlen(s1.data) + strlen(s2.data) + 1];
strcpy(temp.data, s1.data);
strcat(temp.data, s2.data);
return temp;
}
int main(int argc, const char * argv[])
{
String s("herer");
s.print();
String s2 = s;
s2.print();
String s3 = s + s2; // COMPILATION ERROR
return 0;
}
There are several errors with your code. First
String(char * str)
Needs to be
String(const char * str)
If you want to use string literals like you do in main() with String s("herer");. Secondly Your copy constructor
String(String & rhs)
Needs to take be
String(const String & rhs)
So that it can bind to temporaries.
Third you need to include <cstring> or <string.h> in order to use strcpy().
Lastly you are using the wrong delete in you destructor. When you call new you use delete and when you use new[] you call delete []. You use new[] to allocate data but you are deleting it with delete.
You can see a working example of your code here
The argument of the copy constructor must be const&.
String(String const & rhs) : String() { ... }
I have a piece of code as follows:
char* foo(char* str1)
{
str1 = "Some other text";
cout << "String Inside Function::" << str1 << endl;
return str1;
}
int main()
{
char* str = "This is a string";
cout << "String Before Function call::" << str << endl;
foo(str);
cout<<"String After Function call::"<<str<<endl;
return EXIT_SUCCESS;
}
But the cout after my function call gives me "This is a string" even though I have changed it in my foo function. I'm confused here although I know it has got something to do with me not passing the correct address.
You are changing the pointers value and not what it points to.
// Adding & (aka pass by reference) after the char* you can modify the pointer that you passed into foo function
// but you must understand that it was another place in memory!!!
void foo(char*& str1)
{
// This string saved in global section and here you
// changed not the text in the str1 but the pointer itself
str1 = "Some other text";
cout << "String Inside Function::" << str1 << endl;
}
// Another way to change pointer itself is pass a pointer to the pointer
void foo_v2(char** str1)
{
// You should dereference pointer to pointer (* before str1)
*str1 = "Some other text";
}
// In this case you change the content at the pointer str1
// It very dangerous, you can replace content not only the memory under str1
// but even return address if string placed in stack memory.
// Such terrible things occurred if your string that copied into str1
// occupied more bytes then it can be contained in str1
// More safe way is using for example std::string
void foo2(char* str1)
{
char *s = "Some other text";
strcpy(str1, s, strlen(s));
}
int main()
{
char* str = "This is a string";
cout << "String Before Function call::" << str << endl;
foo(str);
cout<<"String After Function call::"<<str<<endl;
return EXIT_SUCCESS;
}
When you pass a pointer by copy, the function you call gets a copy of that pointer, just as passing by copy of any other type means you can't modify the original, passing by pointer works in the same way.
void f(char* copy)
{
// the copy here is modified to point to test
copy = "Test";
}
void g(char* copy)
{
g[0] = 'C';
}
int main()
{
char* p = nullptr;
// f gets a copy of p, p cannot be modified by f
f(p);
char p2[5] = "copy";
// g gets a copy of p2, the value of p2 (the pointer) cannot be modified by g
// however, g can modify what p2 points to
g(p2);
// prints "Copy" (not "copy")
cout << p2;
}
If you want to change what a pointer "points to" via a function, you have to do that in the same way that you would change any other type passed to a function. Either through a pointer (double pointer in this case) or reference (preferred).
void make_c(char** c)
{
*c = new char[10];
}
void make_c(char*& c)
{
c = new char[10];
}
int main()
{
char* t = nullptr;
// via pointer (double pointer in this case)
make_c(&t);
delete [] t;
// via reference
make_c(t);
delete [] t;
}