Copy constructor and const - c++

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.

Related

How do you modify an array of char after it's been declared in a class?

I have a header file that declares a class, and a member variable of type pointer to char,
#pragma once
#include <stdio.h>
class MyClass {
public:
MyClass();
char *myString;
};
and a .cpp file that defines the class and its constructer, and defines the member variable within the constructer.
#include "header.h"
MyClass::MyClass() {
myString = "Hello";
}
int main() {
MyClass myClass;
printf(myClass.myString);
return 0;
}
myString = "Hello" doesn't work, it gives the error 'argument of type "char" is incompatible with parameter of type "char *"'. How do I modify the array after it's been declared?
If you insist on using old, 'C-style' character strings, then you should declare your myString array as an actual array rather than a pointer to unallocated memory:
class MyClass {
public:
MyClass();
char myString[MAXLENGTH]; // Where MAXLENGTH is what you decide is the max size
};
And then, in your constructor, use the strcpy function to initialize the array:
MyClass::MyClass() {
strcpy(myString, "Hello");
}
An alternative, in order to keep the char *myString version, would be to explicitly allocate the memory in the constructor (not forgetting to delete[] the array in the destructor):
class MyClass {
public:
MyClass();
~MyClass();
char *myString;
};
//...
MyClass::MyClass() {
myString = new char[MAXLENGTH];
strcpy(myString, "Hello");
}
MyClass::~MyClass() {
delete[] myString;
}
But, as you're using C++, you'd be far better off using std::string and the many operations that standard class provides.
String literal "Hello" is of type const char[6] or const char* if you will, whereas your myString variable is of type char*. Up to C++03 this implicit conversion was tolerated and you could have:
char* mystring = "Hello";
Starting with C++11, that is no longer the case. You will need the appropriate const char* type instead:
char const* mystring = "Hello";
That being said, in C++ you should use the std::string type instead:
std::string mystring = "Hello";

Constructor was called when assignment operator not implemented

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.

C++ - calling to constructor on exist object

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;
}
};

Direct initialization of a pointer

I read some sample code in the book C++ Primer:
I do not understand how the direct initialization of a pointer works in ps(new std::string(s)) and ps(new std::string(*p.ps)).
If ps is a pointer to string, then why is it ok to place a string as the parameter inside of its direct initialization constructor?
class HasPtr {
public:
HasPtr(const std::string &s = std::string()):
ps(new std::string(s)), i(0) { }
// each HasPtr has its own copy of the string to which ps points
HasPtr(const HasPtr &p):
ps(new std::string(*p.ps)), i(p.i) { }
HasPtr& operator=(const HasPtr &);
~HasPtr() { delete ps; }
private:
std::string *ps;
int i;
};
The new expression creates space for an std::string on the heap and returns a pointer to it. Hence, it is not a string that ps is initialized with, but a pointer to string, which ps is a type of.
HasPtr::ps is a std::string*.
If I dereference a std::string* like this *ps I get the actual std::string object.
If I have an object const HasPtr p I can get it's member pointer to std::string with: p.ps. This is only possible inside class HasPtr because ps is private.
If I do *p.ps I will get the std::string pointed to by p's member std::string*.
So the code:
HasPtr(const HasPtr &p):
ps(new std::string(*p.ps)), i(p.i) {}
Is using the std::string copy constructor to initialize the dynamically created std::string that will be assigned to this->ps by the HasPtr copy constructor.

Const data type cannot be assigned into a non-const data type

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".