I am practicing 'String' class implementation (C++) in Visual Studio 2015.
I have 3 constructors in my class and not any assignment operator.
String();
String(char _c);
String(const char* _pc);
In main(), i am deliberately using assignment operator to check behavior of the code.
To my surprise, it is not giving any error and uses the constructor String(const char* _pc) to assign value to the object.
Also, at the end of the scope, it is calling the destructor twice.
What is compiler doing behind the curtain in this case? and why?
Here is my code:
class String {
private:
int capacity;
char* start;
public:
//Constructors
String();
String(const char* _pc);
//Destructor
~String();
}
String::String() :start(nullptr), capacity(0) {}
String::String(const char* _pc) :capacity(1) {
const char* buffer = _pc;
while (*(buffer++))
capacity++;
int temp_capacity = capacity;
if (temp_capacity)
start = new char[temp_capacity];
while (temp_capacity--) {
start[temp_capacity] = *(--buffer);
}
}
String::~String() {
if (capacity == 1)
delete start;
if (capacity > 1)
delete[] start;
}
int main() {
String s;
s="Hello World";
return 0;
}
What is compiler doing behind the curtain in this case?
Given s="Hello World";,
A temporary String is constructed (implicitly converted) from "Hello World" via String::String(const char*).
s is assigned from the temporary via implicitly-declared move assignment operator (String::operator=(String&&)).
BTW you might mark String::String(const char*) explicit to prohibit the implicit conversion which happened at step#1.
Related
Here is the code i'm having problem with
class MyString {
char* str;
public:
MyString(const char* _str) : str(_str) {}
}
This is the error message from the compiler
error: invalid conversion from ‘const char*’ to ‘char*’ [-fpermissive]
Is this because str is char* and _str is const char*?
Should the copy( I mean str(_str) ) be the exact same type (REGARDING const)?
How could I modify this code into reasonable code? I want to keep MyString(const char* _str) as const char*.
Short answer: yes.
Longs answer:
const char* str is a pointer to a region in memory that contains a c string. By marking it const you are saying that it may not be changed. You are attempting to create a new pointer to the same memory region, but this new pointer says that region may be changed, which the compiler complains about (since the original says that this is not allowed).
Note that you are currently not copying the contents to the string (which I suspect you want), but the pointer to the string. To actually copy the string you must first allocate memory and then copy the contents. Or just use a std::string instead, which manages all this for you.
Yes, you cannot assign a const char* pointer (a pointer-to-const) to a char* pointer (a pointer-to-non-const), they are not compatible.
If you don’t mind your class pointing to the original character data as-is, then you can make the str member be a pointer-to-const:
class MyString {
const char* str;
public:
MyString(const char* _str) : str(_str) {}
};
You will just have to make sure the original character data outlives your MyString object.
This is likely not what you want. The other option is to make your class copy the character data into itself:
class MyString {
char* str = nullptr;
public:
MyString(const char* _str) {
if (_str) {
str = new char[strlen(_str) + 1];
strcpy(str, _str);
}
}
...
};
Just be sure to follow the Rule of 3/5/0 to manage that copy correctly. That means implementing a destructor to free the copy, and a copy constructor and copy assignment operator to make copies of the data, and a move constructor and move assignment operator to move the data around:
class MyString {
char* str = nullptr;
public:
MyString() = default;
MyString(const char* _str) {
if (_str) {
str = new char[strlen(_str) + 1];
strcpy(str, _str);
}
}
MyString(const MyString &src) : MyString(src.str) { }
MyString(MyString &&src) {
str = src.str;
src.str = nullptr;
}
~MyString() {
delete[] str;
}
MyString& operator=(MyString rhs) {
MyString tmp(std::move(rhs));
std::swap(src, tmp.src);
return *this;
}
};
A better solution is to use std::string instead, which handles all of these details for you.
I saw some code that reconsruct Object on c++.
from GeeksForGeeks :
#include<iostream>
#include<string.h>
using namespace std;
class String
{
char *p;
int len;
public:
String(const char *a);
};
String::String(const char *a)
{
int length = strlen(a);
p = new char[length +1];
strcpy(p, a);
cout << "Constructor Called " << endl;
}
int main()
{
String s1("Geeks"); // line 1
const char *name = "forGeeks";
s1 = name; // line 3
return 0;
}
Now, after line 3 s1 is a different object?
I always thought that after you construct object, you can't dereference it.
What you see is an assignment.
A copy assignment operator is automatically generated in your class, since you don't define one yourself. From the page I linked to:
If no user-defined copy assignment operators are provided for a class
type (struct, class, or union), the compiler will always declare one
as an inline public member of the class.
There is actually no dereferencing on line 3: there is only a replacement of an object with an another. The line 3 calls the constructor of String and assign the object to s1. So two different instances of String are being created but the constructor is not ´recalled´ on the first object but assign the created one to s1. The operator = is used as its default behavior which is to find if there is a constructor that matches the type given to the = operator.
On a side note, the dynamic memory is not freed at any point in the code making it a bad code sample.
Let's break it down.
s1 = name;
First, the compiler will construct a new String object using the constructor String(char const*). Then it uses the copy assignment operator to update s1 with this newly created object.
Since you did not define this operator, it simply does a copy of the class members, i.e. p and len: that's the default implementation the compiler generates for you. The old object is not cleaned up before so you are leaking memory... You should thus write your copy semantics using the copy and swap idiom:
class String {
char *p;
int len;
public:
// Your constructors...
// Swap function
friend void swap(String& s1, String& s2) {
using std::swap;
swap(s1.p, s2.p);
swap(s1.len, s2.len);
}
String(String const& other) : p(new char[other.len]()), len(other.len) {
std::copy(other.p, other.p + len, p);
}
String& operator=(String other) {
swap(*this, other);
return *this;
}
// And finally, a destructor:
/* virtual */ ~String() {
delete [] p;
}
};
When I remove The Destructor from code the output come as i desired but if i free the space manually program become mad :( Please some one help me, I'm Using Code::Blocks IDE and Running in Linux mint OS
#include<iostream>
#include<cstring>
using namespace std;
class str
{
char *p;
int len;
public:
str() {
len=0;
p=NULL;
}
str(const char *s);
str(const str &s);
~str() {
cout<<" Distructor Called ";
delete p;
}
friend str operator+(const str &s,const str &t);
friend bool operator<=(const str &s,const str &t);
friend void show(str &s);
};
str::str(const char *s)
{
len=strlen(s);
p=new char[len+1];
strcpy(p,s);
}
str::str(const str &s)
{
len=s.len;
p=new char[len+1];
strcpy(p,s.p);
}
void show(str &s)
{
cout<<s.p;
}
str operator+(const str &s,const str &t)
{
str tem;
tem.len=s.len+t.len;
tem.p=new char[tem.len+1];
strcpy(tem.p,s.p);
strcat(tem.p,t.p);
return tem;
}
bool operator<=(const str &s,const str &t)
{
if(s.len<=t.len)
return true;
else
return false;
}
int main()
{
str s1="New ";
str s2="York";
str s3="Delhi";
str string1,string2,string3;
string1=s1;
string2=s1+s2;
string3=s1+s3;
cout<<"\nString1 = ";
show(string1);
cout<<"\nString2 = ";
show(string2);
cout<<"\nString3 = ";
show(string3);
cout<<"\n\n";
if(string1<=string2) {
show(string1);
cout<<" Smaller Than ";
show(string2);
cout<<"\n";
} else {
show(string3);
cout<<"Smaller Than ";
show(string1);
cout<<"\n";
}
return 0;
}
Read about the Rule of Three.
When you don't declare the assignment operator, a default one is generated by the compiler which does the following:
Assign all the object's members from the corresponding members of the assignment operator's argument, calling the copy assignment operators of the object's class-type members, and doing a plain assignment of all non-class type (e.g. int or pointer) data members.
First of all, the above bold text applies to char *p in your class.
In your operator+ function, tem is an object on stack. When the function ends, tem goes out of scope, and its destructor is called.
So what happens is string1's p is assigned tem's p as per the default assignment operator generated by the compiler, meaning string1's p points to the same memory location as tem's p, which was deallocated after it went out of scope! Hence, string1 does not have the expected value. Later, when string1 goes out of scope and its destructor is called, delete is called on the same memory location for the second time, hence leading to the error shown. Similarly, for string2.
Things will be fine if you overload the assignment operator like this:
void str::operator=(const str&s) {
delete[] p;
len=s.len;
p=new char[len+1];
strcpy(p,s.p);
}
In this case, tem's p will be copied over before its destructor is called.
NOTE:
It works when you remove the destructor because the default
destructor generated by the compiler does not deallocate the
allocated memory, but this will leak memory, which is BAD.
There is another major flaw in your code. Use delete[] for
deallocating arrays.
You have not overloaded the assignment operator. Because of this the same pointer getting assigned and getting deleted twice which is causing the exception.
I am trying to learn about when and where the constructors are called in the code.
I made my own, simple though, stringclass that has these possibilities:
String string1("hello world");
string1 = "Hello march!!!";
Concerning the latter one, these two constructors where called in the String-class
Called in order...
converting-constructor
copy-constructor
I can understand why the copy-constructor is called not really why the converting-constructor is called?
Here are these two constructors:
converting-constructor
String::String(const char* ch) : _strPtr(0) {
_strLen = strlen(ch) + 1;
_strPtr = new char[_strLen];
strncpy(_strPtr, ch, _strLen);
_strPtr[_strLen - 1] = '\0';
cout << "konverterings-constructor\n";
}
copy-constructor
String::String(const String& string) {
_strLen = strlen(string._strPtr) + 1; // behöver ingen "djup-kopia" av vektorlängden.
if(string.getString()) {
_strPtr = new char[_strLen];
strncpy(_strPtr, string._strPtr, _strLen);
_strPtr[_strLen - 1] = '\0'; // null-terminering
} else {
_strPtr = 0;
}
cout << "copy-constructor\n";
}
overloading member-function of assignment-operator
String String::operator=(const String& string) {
if (this == &string) { // kontrollera om objektet är lika med sig självt.
return *this;
}
cout << "......\n";
delete [] getString();
_strLen = strlen(string.getString()) + 1;
if(string.getString()) {
_strPtr = new char[getLength()];
strncpy(_strPtr, string.getString(), _strLen);
_strPtr[_strLen - 1] = '\0'; // null-terminering
} else {
_strPtr = 0;
}
return *this;
}
I can understand why the copy-constructor is called not really why the converting-constructor is called?
The converting constructor is called because when you assign, since you don't have an assignment operator that takes const char*, a temporary String is constructed from the RHS using the converting constructor, and used to assign to the LHS.
Note that the copy is down to the fact that your assignment operator returns by value. This is unusual for an assignment operator. Usually these return a reference.
Okay, first the array of const chars (which is the type of the string literal) decays to a pointer to const char. Then the converting ctor is called to construct a String from a const char *. Then copy ctor is called to construct a String in the return from your assignment operator. You should return by non-const reference to be idiomatic.
I am trying to make a really simple string class with a copy constructor, the problem that i am having is that i am trying to send a data type/object as a constant but whenever i try to assign it to the same non-constant data, the compiler complains.
I don't get why would compiler complain, i am just trying to copy the data, i am not modify it.
Header
class MyString{
private:
char* thestring;
MyString* ms;
public:
MyString();
MyString(const char* str);
MyString(const MyString& str);
~MyString();
MyString& operator=(const char* str);
};
CPP
MyString::MyString(){
thestring = '\0';
}
MyString::MyString(const MyString& str){
ms = str;
}
MyString& MyString::operator=(const char* str){
MyString* temp = new MyString();
temp->thestring = str;
return temp;
}
MyString::MyString(const char* str){
}
MyString::~MyString(){
}
here are the erros:
no suitable conversion function from "const MyString" to "MyString *"
exists 9
a value of type "const char *" cannot be assigned to an entity of type
"char *" 14
a reference of type "MyString &" (not const-qualified) cannot be
initialized with a value of type "MyString *" 15
'MyString::operator=' : must return a value 16
First, you do not need ms member in your class: it serves no purpose, and it is used only in your (incorrect) copy constructor. Remove ms, and rewrite the code as follows:
MyString::MyString(const MyString& str) {
thestring = strdup(str.thestring);
}
Note that now your code violates the rule of three: once you add a copy constructor, you also need a matching assignment operator and a destructor:
MyString& MyString::operator=(const MyString& str) {
if (this != &str) {
free(thestring);
thestring = strdup(str.thestring);
}
return *this;
}
MyString::~MyString() {
free(thestring);
}
Finally, I would rewrite your default constructor like this:
MyString::MyString()
: thestring(0) {
}
Essentially, this is not different, but an initialization list is more canonical.
Line 9: ms is a MyString*, but str is just a const MyString& (ie, not a pointer).
Line 14: You cannot assign a const char* value to a char*, as this would potentially allow you to change it:
void foo(const char* dont_change_this) {
char* s = dont_change_this;
s[0] = '!'; // you changed it!
}
Line 15: Once again, a simple pointer mis-match. The return type of op= is MyString&, but temp has type MyString*.
But you're not just trying to copy the data, you're trying to strip it of its constness. In other words, you're attempting to assign a non-const pointer with a const pointer. This would give you the ability to potentially change the value of what you intended to be "const".