The debugger is giving me 'bad ptr' when I create a new string array in this constructor, but only when my overloading operator method creates a new MyString object... confused.
Here is my constructor
MyString::MyString()
{
stringSize = 0;
stringCap = 16;
stringArray = new char[stringCap + 1];
stringArray[0] = '\0';
}
Here is my overloading operator method
MyString operator+(const char* leftOp, const MyString& rightOp)
{
MyString result; // new object used to store result
result.stringSize = strlen(leftOp) + rightOp.stringSize;
// if the string does not fit in the array
if( result.stringSize > result.stringCap )
{
delete[] result.stringArray;
result.stringCap = ( result.stringSize + 15 ) & ~15;
result.stringArray = new char[result.stringCap + 1];
}
strcpy(result.stringArray, leftOp);
strcat(result.stringArray, rightOp.stringArray);
return result;
}
Here is my copy constructor, which the debugger never gets too
MyString::MyString(const MyString& s)
{
stringSize = s.stringSize;
stringCap = s.stringCap;
//stringArray[stringCap + 1];
stringArray = new char[stringCap + 1];
stringArray = s.stringArray;
}
Well, when this method returns, "result" is going to be copied and the original destructed. If the destructor deletes the array, and there isn't a smart copy constructor which ensures that the new copy includes a valid new array, then you're going to have problems.
But you said the compiler says something about a bad pointer -- where? What line?
Since from your code snippet, nothing seems wrong, my sixth sense tells me that you've NOT written copy-constructor, and are working with the default one generated by the compiler, or possibly stringArray is not a null-terminated string!
EDIT:
In your copy-constructor, this is wrong:
stringArray = s.stringArray; //wrong!
Use strcpy instead:
strcpy(stringArray, s.stringArray); //correct!
Make sure all your strings are null-terminated!
Related
I have a class that contains the operator + () to add 2 objects. The class contains an attribute char * variable str that points to an allocated char array from new char[] that contains a C style text string. I want the operator +() to concatenate two char arrays into a third char array allocated as a new buffer sized to contain the concatenated string as the result of the operator.
I am using overloaded constructor and initializing list and I initialize the variable to a default value. The destructor I've added to the class frees the memory allocated for the object text buffer in the char *str variable.
Everything works perfectly fine WITHOUT the class destructor, but as soon as I add the destructor, the program prints weird characters. I am also seeing a memory leak.
Here is the code and note that the class destructor is included here!
#include <iostream>
#include <cstring>
#include<cstring>
class Mystring {
private:
char *str;
public:
Mystring():
str{nullptr} {
str = new char[1];
*str = '\0';
}
Mystring(const char *s):
str {nullptr} {
if (s==nullptr) {
str = new char[1];
*str = '\0';
} else {
str = new char[std::strlen(s)+1];
std::strcpy(str, s);
}
}
Mystring(const Mystring &source):
str{nullptr} {
str = new char[std::strlen(source.str)+ 1];
std::strcpy(str, source.str);
std::cout << "Copy constructor used" << std::endl;
}
~Mystring(){
delete [] str;
}
void print() const{
std::cout << str << " : " << get_length() << std::endl;
}
int get_length() const{
return std::strlen(str);
}
const char *get_str() const{
return str;
}
Mystring&operator+(const Mystring &dstr)const;
};
// Concatenation
Mystring &Mystring::operator+(const Mystring &dstr)const {
char *buff = new char[std::strlen(str) + std::strlen(dstr.str) + 1];
std::strcpy(buff, str);
std::strcat(buff, dstr.str);
Mystring temp {buff};
delete [] buff;
return temp;
}
int main() {
Mystring m{"Kevin "};
Mystring m2{"is a stooge."};
Mystring m3;
m3=m+m2;
m3.print();
return 0;
}
Mystring &Mystring::operator+(const Mystring &dstr)const
This states that it's going to return a reference to a MyString Your temp however is local. This results in a far more serious issue than just a memory leak.
Once you're returning by value; you'll find that it might end up wanting to use the copy or move constructor; which leads to (as said in the comments) the rule of 3 (or rule of 5).
This
Mystring &Mystring::operator+(const Mystring &dstr)const {
char *buff = new char[std::strlen(str) + std::strlen(dstr.str) + 1];
std::strcpy(buff, str);
std::strcat(buff, dstr.str);
Mystring temp {buff};
delete [] buff;
return temp;
}
contains a bug. You create a temp object and return it by reference (Mystring&). That reference turns into a dangling one as soon as the function returns and using it is undefined behaviour. The fact that the code appears to work with or without the destructors means nothing. Undefined behaviour is undefined and you cannot expect anything from the code that invokes it.
You may simply change the return type of Mystring::operator+ to return by value (simply Mystring).
Although, you would encounter another problem later on.
m3=m+m2;
performs a copy assignment, which uses T& operator = (const T&). You did not provide one, so the compiler generated one.
Why is that bad? Because automatically generated special member functions perform shallow copies (if they are used for copying), which means that if you have, for example, pointer member variable, the pointer will be copied. Just the pointer. Not the data it points to. Just the pointer. You end up with two pointers pointing to the same memory and once a correctly defined destructor tries to free those resources, you encounter a double delete[] error, which, again, is undefined behaviour for non-nullptr pointers.
You may want to look forward into the rule of three, and then probably into the rule of five.
With regard to this line:
The s[9] that goes through void setChar(size_t index, char c), but what is *this (inside the if, in the line: ``)?
Is it s1 or nullptr? Then what is _str (in the same line: *this = _str;), is it s1?
class MyString {
size_t _size = 0;
char* _str = nullptr;
int* _pRefCount = nullptr;
}
operator char() const {
return ((const MyString&)_s)[_index];
}
};
void detach() {
if(_pRefCount && --*_pRefCount == 0) {
delete []_str;
delete _pRefCount;
}
}
void attach(const MyString& str) {
_size = str._size;
_str = str._str;
_pRefCount = str._pRefCount;
++*_pRefCount;
As always *this is the object the function is executed for. If you are inside s1[4] the *this would be s1.
Using *this = _str; is just an involved way of calling one of the operator= overloads for MyString. It has the effect of "unsharing" by assigning a new copy of _str to itself.
Also, it has been known for quite some time that reference counted strings is not an optimization. For example in the output of s1[0] the code will create an unnecessary CharProxy just in case someone would assign a value to it. You also get a separate memory allocation for the refcount of each string you create, even if you never intend to share it
Whenever a the String Destructor is hit it Triggers a Breakpoint, i think i might be deleting the same variable twice or not assigning the right amount of memory during the manipulation of m_str
#include "String.h"
#include <iostream>
using namespace std;
String::String()
{
m_str = nullptr;
}
String::String(const char* newStr)
{
m_str = new char[strlen(newStr)+ 1];
strcpy(m_str, newStr);
}
String::~String()
{
if (m_str != nullptr)
{
delete[] m_str;
}
}
void String::operator=(const String & myString)
{
if (m_str != nullptr)
{
delete[] m_str; //Breakpoint Apears Here
};
m_str = new char[strlen(myString.m_str) + 1];
m_str = myString.m_str;
}
void String::operator=(char* newStr)
{
if (m_str != nullptr)
{
delete[] m_str;
};
m_str = new char[strlen(newStr) + 1];
m_str = newStr;
}
}
Assigning one const char * to another as you do e.g. In the assignment operators doesn't copy the string, it just copies the pointer. So instead of two separate strings, you now have two pointers, pointing to the same string.
So for one, you are leaking the newly created char array and second, you are trying to delete whatever has been passed to the assignment operator, which in your case is probably a string literal instead of something that was created via new. Also in the case of the copy assignment operator, you would end up with two String objects pointing to the same char array and thus double deletion.
To solve this Problem, just use strcpy as you did in the constructor.
You correctly use a strcpy to copy chars from original char array in String::String(const char* newStr), but in all other places in your code, you wrongly write:
m_str = new char[strlen(myString.m_str) + 1];
m_str = myString.m_str;
First line correctly allocates an array of correct size, but the second erases the pointer obtained by new so:
you get a memory leak, since the allocated memory has no pointer on it any longer and will never be freed
the m_str member points to the original char array. If it is later deleted you have a dangling pointer, and if you delete this instance first, you will try to delete the original char pointer in another instance making it dangling, or even try to delete a static or local array.
TL/DR: consistently copy the character array with strcpy (or std::copy)
For example, imagine a + operator overload to concatenate two strings; in this example, the names of two cheeses. Here I use a tmp variable created on the heap. Does this cause a memory leak when the method exits?
Class Cheese {
private:
int nameLen; // Length of name
char * name; // C string
public:
Cheese(char * str); // Constructor
int getNameLen(); // Returns length of name
char * getName(); // Returns name
}
}
Cheese Cheese::operator + (const Cheese & rhs) {
char * tmp = new char[nameLen + rhs.getNameLen()];
strcpy(tmp, name);
strcat(tmp, rhs.getName());
return Cheese(tmp); // Is this a problem? Where does tmp get deleted?
}
Usage:
Cheese c1("mozzarella");
Cheese c2("provolone");
printf("Cheese Blend: %s", (c1 + c2).getName());
So, the return value creates another instance of Cheese. My concern is whether tmp, created on the heap, creates a memory leak. It is never deleted, and since I am including it as part of my return statement, I don't know where I should delete it.
Is this a legitimate concern? Do I need this tmp variable, or can I be clever and not use it at all? Is it best practice to just put it on the stack, as follows? What's the best practice?
char * tmp[len + rhs.getLen()];
Thx!
return Cheese(tmp); // Is this a problem? Where does tmp get deleted?
It is a problem if the constructor of Cheese makes a copy of the input argument.
You can use a smart pointer to delete the memory used by tmp when the function returns.
int len = nameLen + rhs.getNameLen() + 1; // Need the +1 for the null terminator
std::unique_ptr<char, std::default_delete<char[]>> tmp(new char[len]);
strcpy(tmp.get(), name);
strcat(tmp.get(), rhs.getName());
return Cheese(tmp.get());
You can do away with these unnecessary complexity in your code if you use std::string for type of the member variable name instead of char*. If you do that, you can get rid of the nameLen member variable. Then, the above function can be simplified to:
Cheese Cheese::operator + (const Cheese & rhs) {
return Cheese(this->name + rhs.name);
}
I have these variables:
char** wordList_;
int wordListCapacity_;
int* wordCountList_;
char* fileName_;
int nUniqueWords_;
int nTotalWords_;
int nTotalCharacters_;
My copy constructor:
FileIndex::FileIndex(const FileIndex& fi)
{
fileName_ = new char[strlen(fi.fileName_) + 1];
strcpy(fileName_, fi.fileName_);
cout << "Jiasd?" << endl;
wordListCapacity_ = fi.wordListCapacity_;
nUniqueWords_ = fi.nUniqueWords_;
nTotalWords_ = fi.nTotalWords_;
nTotalCharacters_ = fi.nTotalCharacters_;
wordList_ = new char*[wordListCapacity_];
wordCountList_ = new int[wordListCapacity_];
for(int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = fi.wordList_[i];
wordCountList_[i] = fi.wordCountList_[i];
}
}
My overloaded assignment operator:
FileIndex& FileIndex::operator=(const FileIndex& fi)
{
fileName_ = new char[strlen(fi.fileName_) + 1];
strcpy(fileName_, fi.fileName_);
wordListCapacity_ = fi.wordListCapacity_;
nUniqueWords_ = fi.nUniqueWords_;
nTotalWords_ = fi.nUniqueWords_;
nTotalCharacters_ = fi.nTotalCharacters_;
wordList_ = new char*[wordListCapacity_];
wordCountList_ = new int[wordListCapacity_];
for (int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = new char[strlen(fi.wordList_[i])+1];
strcpy(wordList_[i], fi.wordList_[i]);
wordCountList_[i] = fi.wordCountList_[i];
}
return *this;
}
Whenever I create a FileIndex (called FirstIndex) and initialize the member variables with something meaningful (not NULL) I have these lines to test the copy constructor and assignment operator:
FileIndex secondIndex = firstIndex;
FileIndex thirdIndex;
secondIndex = thirdIndex; // Segmentation fault here
I get a segmentation fault with the assignment operator but I have a feeling it may be because of faulty code in the copy constructor. That being said, if there's an error in the copy constructor then there's also probably one in the assignment operator.
Thanks in advance for the help!
I think you want to use std::string and std::vector<T> for your class. Also, for the purpose of what goes wrong it would be necessary to see the default constructor and the destructor. From the look of you setup it seems that you may e.g. not have initialized some of the members in your default constructor. Also, you assignment operator has several resource leaks and will be pretty bad off if you try self assignment. In general, I'd recommend to implement assignment operators like this:
T& T::operator= (T other) {
other.swap(*this);
return *this;
}
This leverages the work done for the copy constructor and use a swap() member which is generally very easy to do.
Check out your copy constructor.
for(int i = 0; i < nUniqueWords_; i++) {
wordList_[i] = fi.wordList_[i];
wordCountList_[i] = fi.wordCountList_[i];
}
The problem is with wordList_[i] = fi.wordList_[i];. You are not allocating new memory and doing a strcpy here as you do in the assignment operator. Instead your new copy is actually pointing to data from the instance that it is copying from. I believe this may be what David Schwartz was alluding to.
It looks as if you may not initialize wordListCapacity_ correctly (it's hard to tell since you don't show the default ctor). Since it is an int, it can have a negative value, which can cause a segfault when you attempt wordList_ = new char*[wordListCapacity_];. There may be other problems.