I have a problem in my string class. After cin.get compiler display me this expression. Where did I go wrong?
//Source.cpp
#include <iostream>
#include <string>
#include "str.h"
using namespace std;
int main()
{
str S1 = "Hello, world!";
str S2 = "LOL";
str S3 = S2;
cout << S3.cstr() << endl;
cout << S1.size() << ": " << S1.cstr() << endl;
cin.get();
}
//str.h
#ifndef STR_H
#define STR_H
class str
{
public:
str(const char* = "");
~str();
void operator=(const char*);
void operator=(str);
const char* cstr();
int size();
private:
void newString(const char*);
char* charPtr;
int Size;
};
#endif
//str.cpp
#define _CRT_SECURE_NO_WARNINGS
#include <cstring>
using std::strlen;
using std::strcpy;
#include "str.h"
str::str(const char* cstr)
{
newString(cstr);
}
str::~str()
{
delete[] charPtr;
}
const char* str::cstr()
{
return charPtr;
}
void str::newString(const char* cstr)
{
delete[] charPtr;
Size = strlen(cstr);
charPtr = new char[Size + 1];
strcpy(charPtr, cstr);
}
void str::operator=(const char* cstr)
{
newString(cstr);
}
int str::size()
{
return Size;
}
You didn't obey the rule of three. You have user defined destructor and copy assignment operator, but you haven't defined a copy constructor. The compiler helpfully</sarcasm> defined one for you.
On this line:
str S3 = S2;
You copy initialize S3. As you can see from the rules in the linked page:
If T is a class type and the type of other is cv-unqualified version of T or a class derived from T, the constructors of T are examined and the best match is selected by overload resolution. The constructor is then called to initialize the object.
The best matching constructor happens to be the str(const str&); which was added by the compier. The default copy constructor does not make a copy of the data pointed by the charPtr but just copies the pointer instead.
When S2 goes out of scope, after cin.get();, it's destructor deletes S2.charPtr. Next, S3 is destroyed and it's destructor tries to delete S3.charPtr which has the same value as S2.charPtr and is therefore already deleted. This has undefined behaviour. Quick googling suggests that _BLOCK_TYPE_IS_VALID assertion fails if heap pointer is invalid. I'm guessing it's likely result of this undefined behaviour.
Solution: Implement the copy constructor str(const str&); so that copies don't share data.
Related
Here I have a C++ code:
#include <iostream>
#include <map>
#include <string>
#include <cstdlib>
using namespace std;
class Person {
private:
int year;
Person(const Person& pers);
public:
Person(int y): year(y)
{ cout << "Default constructor" << endl;}
~Person()
{
cout << "Destructor " << endl;
}
int get_year() const
{
return year;
}
};
int main()
{
map<string, Person*> test;
test.insert(pair<string, Person*>("ini_1", new Person(2)));
return 0;
}
Output
Default constructor
From the output, I would like to know, how I can delete the value of test map given new Person(2) without coding it like first
Person* per = new Person(2)
test.insert(pair<string, Person*>("ini_1", per));
delete per;
Without defining like this first
Person* per = new Person(2)
test.insert(pair<string, Person*>("ini_1", per));
Will it lead to undefined behaviour? Can you describe more detail of the undefined behaviour? Especially how does it exist in the memory? Thanks.
If it is not correct, can I do like this which use Person instead new Person? Will it lead to any undefined behaviour?
#include <iostream>
#include <map>
#include <string>
#include <cstdlib>
using namespace std;
class Person {
private:
int year;
public:
Person(int y): year(y)
{ cout << "constructor" << endl;}
Person(const Person& pers)
{
cout << "copy constructor" << endl;
}
~Person()
{
cout << "Destructor " << endl;
}
int get_year() const
{
return year;
}
};
int main()
{
map<string, Person> test;
test.insert(pair<string, Person>("ini_1", Person(2)));
return 0;
}
Output:
constructor
copy constructor
copy constructor
Destructor
Destructor
Destructor
I don't understand why the constructor ran for once and copy constructor ran for twice. Can you please explain where they happened?
Thanks.
From the output, I can see the destructor did not run. I would like to
know, how I can delete the new pointer without defining it?
You declared a map of ;pointers to Person
map<string, Person*> test;
So an object of the type Person is created only once in this statement
test.insert(pair<string, Person*>("ini_1", new Person(2)));
Further the map deals with the pointer not with the object.
You will need to delete the created object explicitly. For example
for ( auto &item : test )
{
delete item.second;
item.second = nullptr;
}
If you will not delete the allocated object (or objects) then there will be a memory leak.
I don't understand why the constructor ran for once and copy
constructor ran for twice. Can you please explain where they happened?
In this statement
test.insert(pair<string, Person>("ini_1", Person(2)));
the conversion constructor is called explicitly to create an object of the type Person Person(2).
Then the copy constructor of the class Person is called to create an object of the type pair<string, Person>.
And at last this object is copied to the map again calling the copy constructor of the type Person for the data member second of the pair.
So three objects were created and three destructors for the objects were invoked.
#include <iostream>
#include <cstring>
class AAA {
public:
char* str;
AAA(const char* s) { /* Constructor */ }
AAA operator=(AAA& ref) {
delete[] str;
this->str = new char[strlen(str) + 1];
strcpy(this->str, ref.str);
return *this;
}
};
int main() {
AAA a1("fst");
AAA a2("scd");
a1 = a2;
std::cout << a1.str << "\n";
}
In operator=() if I set return type to object instead of reference(like above code), nothing is output. If I delete return *this; and change return type to void, the value of a1.str is printed well.
My Question is: Why is there a problem if the return type is object although there is no problem when return type is void? Regardless of the type of return value, it seems that copying strings is already done by strcpy(). In this case, I think I don't have to set return type as a reference...
There are couple of issues here.
Your constructor does not do anything.It should initialize str
AAA(const char* s)
{
str = new char[strlen(s)+1];
strcpy(str,s);
}
Then
this->str = new char[sizeof(str) + 1];
should be
this->str = new char[strlen(ref.str) + 1];
otherwise you are strcpying into memory that does not belong to you because sizeof(str) will just give you size of a char * and you try to copy ref.str into this memory.Moreover you should not be deleting the existing object in assignment operator at all.You should update it.
You don't have a destructor to free the memory
~AAA(){delete[] str;}
Here are the inserts of my code...
main.cpp
void addStuff(Journey& journey)
{
journey.addPerson("John Doe", "USA");
}
void demo()
{
Journey journey("Sweden");
addStuff(journey);
std::cout << journey;
}
int main(int argc, char* argv[])
{
demo();
return 0;
}
Journy.cpp
void Journey::addPerson(const char* name, const char* nationality)
{
add(Person(name, nationality));
}
void Journey::add(Person person)
{
persons_.push_back(person);
}
std::ostream& operator<<(std::ostream& out, const Journey& journey)
{
out << "Journey: " << journey.name_ << std::endl;
out << " Persons attending:" << std::endl;
for(Journey::PersonList::const_iterator person_it = journey.persons_.begin();
person_it != journey.persons_.end();
person_it++)
{
out << " " << *person_it;
}
return out;
}
Person.cpp
Person::Person(){}
Person::Person(const char* name, const char* nationality) : name_(0),
nationality_(0)
{
copyString(&name_, name);
copyString(&nationality_, nationality);
}
Person::Person(const Person& other): name_( other.name_),
nationality_( other.nationality_) {}
void Person::copyString(char** dest, const char* source)
{
unsigned int str_len = strlen(source);
char* str = new char[str_len+1];
strncpy(str, source, str_len);
str[str_len] = '\0';
*dest = str;
}
std::ostream& operator<<(std::ostream& out, const Person& person)
{
out << person.name_ << " (" << person.nationality_ << ")" << std::endl;
return out;
}
However, when I try to execute the code, as a result I get:
Persons attending:
P�� ()
I am not really sure what I am doing wrong. Could the problem be the scope and lifetime of variables? As I understood, a list container makes a copy of each entry, so the scope and lifetime shouldn't be the issue. I have also seen somewhere that in order for a class instance to be stored in the list, the class must have default constructor, copy constructor and an = operator overloaded. My class Person has all these characteristics.
The code I posted are just inserts that I found relevant for this issue.
If anyone could give me the slightest hint what the problem could be, it would be really appreciated.
With regards
You do have a copy-constructor, which is good since you do a lot of copying. However, you only do shallow copying, i.e. copy only the pointers and not the actual contents.
This means that if you make a copy of a Person object (like you do when calling Journey::add(Person person)) then you will have two objects both using the same pointers to the same memory. If your destructor (if you have any) free the memory, then the memory is free'd for both objects, but one of the object will still have the pointers to the now free memory, leading to undefined behavior when you try to dereference those pointers.
You need to do deep copying, in other words allocate new memory and copy the contents. Or do the sensible thing and use std::string.
Let's consider the following code (live at: http://ideone.com/3Ky4Kr)
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
class StrStrTest {
public:
StrStrTest(const std::string& ba) {
a = (char*)calloc(1, ba.length() + 1);
strcpy(a, ba.c_str());
}
virtual ~StrStrTest() {
free(a);
}
private:
char* a;
friend std::basic_ostream<char>& operator << (std::basic_ostream<char>& ss, const StrStrTest& a);
friend std::basic_istream<char>& operator >> (std::basic_istream<char>& ss,const StrStrTest& a);
};
std::basic_ostream<char>& operator << (std::basic_ostream<char>& ss, const StrStrTest& a) {
ss << a.a;
return ss;
}
std::basic_istream<char>& operator >> (std::basic_istream<char>& ss,
const StrStrTest& a) {
ss >> a.a; // <<-- HERE
// a.a = NULL;
return ss;
}
int main()
{
StrStrTest bb("foo");
std::cin >> bb;
std::cout << bb;
}
Firstly, why does it compile? On the line marked with <<-- HERE I (subtly) modify a const object. (obviously a.a = NULL; does not compile, that's too obvious).
Secondly does this lead to undefined behaviour?
PS: Please don't consider that the code is not safe, might overwrite memory it does not own, char* vs. std::string, etc... I know these, and it is not the point of the question, this is not production code.
The overload you are using is this: operator>>(std::istream&, char*). It takes a char*, by value. It doesn't modify the pointer (i.e. It doesn't change the pointer to point somewhere else). It modifies the data which the pointer holds the address of, writing a null terminated c-string to that location. So, the const-ness of your object is not being violated, because that data is not part of your object.
If you try to do something like this:
a.a = NULL;
That is modifying a member of your object, and so is not allowed.
Secondly does this lead to undefined behaviour?
It can, if a.a does not point to properly allocated memory with enough space for the next space delimited char data in the stream. But not as a result of anything to do with the const-ness of the StrStrTest object.
I am working on a challenge given to me by a friend and to complete it I need to pass a mutable string into a function without prior declaration. (The function preforms some operations on the string so it must be mutable and due to constraints in the challenge I cannot declare the variable before the function call. Basically can
myFunction("abcdef");
be altered in such a way that the string is declared in the function call and passed or so that the passed string is not declared in non-mutable memory.
Here is a version which changes the call to be
myFunction("abcdef"_ncs);
I guess, this innocent addition for "non-const string" should be permissible. Here is the code:
#include <cstring>
#include <cstddef>
#include <iostream>
void myFunction(char* x) {
std::cout << "x=" << x << "\n";
}
struct tmp_string {
char* buffer;
tmp_string(char const* str, std::size_t n)
: buffer(std::strcpy(new char[n + 1], str)) {
}
tmp_string(tmp_string&& other): buffer(other.buffer) { other.buffer = 0; }
~tmp_string() { delete[] buffer; }
operator char*() { return buffer; }
};
tmp_string operator"" _ncs(char const* str, std::size_t n) {
return tmp_string(str, n);
}
int main()
{
myFunction("abcdef"_ncs);
}
I didn't use std::string primarily because there is no neat conversion from a std::string to a non-const string. The only approach I could think of would be
myFunction(&std::string("abcdef")[0]);
At least, it would also neatly clean up after itself (as does the approach using tmp_string above). Note that starting with C++11 the approach taking the address of the first byte also yields a null-terminated string (for C++03 the string wasn't guaranteed to be null-terminated; since I had trouble verifying this guarantee: it is in 21.4.5 [string.access] paragraph 2).
Here is a simple way to do this.
#include <iostream>
using namespace std;
void MyFunc(char* c)
{
c[0] = 's';
cout << c << endl;
delete[] c;
}
int main()
{
MyFunc(new char[3]{'a','b', 0});
return 0;
}