I have a class that has two field one of them is a pointer and another is int value to hold length of string sets in constructor.
class MyString
{
char* m_pchString;
int m_nLength;
public:
MyString(const char* pchString="")
{
m_nLength = strlen(pchString) + 1;
m_pchString = new char(m_nLength);
strcpy_s(m_pchString,m_nLength, pchString);
}
MyString(const MyString &Source)
{
m_nLength = Source.m_nLength;
if (Source.m_pchString)
{
m_pchString = new char(m_nLength);
strcpy_s(m_pchString,m_nLength,Source.m_pchString);
}
else
{
m_pchString = 0;
}
}
~MyString()
{
delete[] m_pchString;
m_pchString = 0;
}
char* GetString()
{
return m_pchString;
}
int GetLength()
{
return m_nLength;
}
};
Then use it in console application and create an object cHello .afterward create another object an assign it by cHello within a block
int main()
{
MyString cHello ("Hello,World");
{
MyString cCopy = cHello;
}
std::cout << cHello.GetString();
}
When the lifetime of cCopy ends, the destructor of cCopy gives me an error. What is the problem in this code?
The expression m_pchString = new char(m_nLength) allocates one character, and initializes it to the value m_nLength. It does not allocate an array of m_nLength elements.
That means you will go way out of bounds when copying the string into the memory pointed to by m_pchString, and you will have undefined behavior.
If you want to allocate an array or more than one elements you need to use square brackets [], as in
m_pchString = new char[m_nLength];
Related
I am having trouble passing an array of object pointers from main() to a function from different class.
I created an array of object pointers listPin main() and I want to modify the array with a function editProduct in class Manager such as adding new or edit object.
Furthermore, I want to pass the whole listP array instead of listP[index]. How to achieve this or is there any better way? Sorry, I am very new to c++.
#include <iostream>
using namespace std;
class Product
{
protected:
string id, name;
float price;
public:
Product()
{
id = "";
name = "";
price = 0;
}
Product(string _id, string _name, float _price)
{
id = _id;
name = _name;
price = _price;
}
};
class Manager
{
protected:
string id, pass;
public:
Manager(string _id, string _pass)
{
id = _id;
pass = _pass;
}
string getId() const { return id; }
string getPass() const { return pass; }
void editProduct(/*array of listP*/ )
{
//i can edit array of listP here without copying
}
};
int main()
{
int numProduct = 5;
int numManager = 2;
Product* listP[numProduct];
Manager* listM[numManager] = { new Manager("1","alex"), new Manager("2", "Felix") };
bool exist = false;
int index = 0;
for (int i = 0; i < numProduct; i++) { //initialize to default value
listP[i] = new Product();
}
string ID, PASS;
cin >> ID;
cin >> PASS;
for (int i = 0; i < numManager; i++)
{
if (listM[i]->getId() == ID && listM[i]->getPass() == PASS) {
exist = true;
index = i;
}
}
if (exist == true)
listM[index]->editProduct(/*array of listP */);
return 0;
}
Since the listP is a pointer to an array of Product, you have the following two option to pass it to the function.
The editProduct can be changed to accept the pointer to an array of size N, where N is the size of the passed pointer to the array, which is known at compile time:
template<std::size_t N>
void editProduct(Product* (&listP)[N])
{
// Now the listP can be edited, here without copying
}
or it must accept a pointer to an object, so that it can refer the array
void editProduct(Product** listP)
{
// find the array size for iterating through the elements
}
In above both cases, you will call the function as
listM[index]->editProduct(listP);
That been said, your code has a few issues.
First, the array sizes numProduct and numManager must be compiled time constants, so that you don't end up creating a non-standard variable length array.
Memory leak at the end of main as you have not deleted what you have newed.
Also be aware Why is "using namespace std;" considered bad practice?
You could have simply used std::array, or std::vector depending on where the object should be allocated in memory. By which, you would have avoided all these issues of memory leak as well as pointer syntaxes.
For example, using std::vector, you could do simply
#include <vector>
// in Manager class
void editProduct(std::vector<Product>& listP)
{
// listP.size() for size of the array.
// pass by reference and edit the listP!
}
in main()
// 5 Product objects, and initialize to default value
std::vector<Product> listP(5);
std::vector<Manager> listM{ {"1","alex"}, {"2", "Felix"} };
// ... other codes
for (const Manager& mgr : listM)
{
if (mgr.getId() == ID && mgr.getPass() == PASS)
{
// ... code
}
}
if (exist == true) {
listM[index]->editProduct(listP);
}
You cannot have arrays as parameters in C++, you can only have pointers. Since your array is an array of pointers you can use a double pointer to access the array.
void editProduct(Product** listP){
and
listM[index]->editProduct(listP);
Of course none of these arrays of pointers are necessary. You could simplify your code a lot if you just used regular arrays.
Product listP[numProduct];
Manager listM[numManager] = { Manager("1","alex"), Manager("2", "Felix")};
...
for(int i = 0; i < numManager; i++ ){
if(listM[i].getId() == ID && listM[i].getPass() == PASS) {
exist = true;
index = i;
}
}
if(exist == true){
listM[index].editProduct(listP);
}
I have a MultiString class that has some methods in it.
I am strugling with deleting my array of strings in the destructor, I get heap corruption.
Here is my class (some methods are cut)
class MultiString {
public:
//constructor destructor
MultiString(int);
~MultiString();
//methods
void Setat(int nindex, const char* str);
//attributes
char** buf;//pointer to vector
int str_nmb;//strings number
};
Constructor code:
MultiString::MultiString(int number)
{
str_nmb = number;
buf = new char* [number];
for (int i = 0; i < number; i++) buf[i] = NULL;
}
Setat code (used to set strings in array):
void MultiString::Setat(int nindex, const char* str)
{
if (nindex<0 || nindex>str_nmb || str == NULL) {
std::cout << "gg";
return;
}
char* tmp = new char[strlen(str)+1];
if (tmp == NULL) return;
if (buf[nindex] != NULL) delete buf[nindex]; //here delete works fine
buf[nindex] = tmp;
strcpy_s(buf[nindex], strlen(buf[nindex]), str);
std::cout << buf[nindex]<< std::endl;
}
Destructor code:
MultiString::~MultiString()
{
for (int i = 0; i < str_nmb; i++)
delete buf[i]; // heap corruption here
delete buf;
}
And my main():
int main()
{
MultiString* ms = new MultiString(3);
ms->etat(0, "qwerty");
ms->Setat(1, "asdfgh");
ms->Setat(2, "zxcvbn");
delete ms;
return 0;
}
char* tmp = new char[strlen(str)+1];
// ...
buf[nindex] = tmp;
strcpy_s(buf[nindex], strlen(buf[nindex]), str);
buf[nindex] points to the freshly allocated, but uninitialized memory area. Calling strlen on that is undefined behavior and likely what corrupts your heap. You probably want to call strlen(str) instead.
Apart from that you're making a few other mistakes:
Rule of 0/3/5 broken.
delete where delete[] would be necessary
Wrong bounds check (nindex>str_nmb)
What about Multistring(-5) ?
if (tmp == NULL) return; ... no, you shouldn't mute errors. Throw an exception or something.
Also .. why strcpy_s ? Use std::strncpy ... that's at least guaranteed to be available! Make sure the copied C string is null terminated!
I am not allowed to make use of the vector class so I need to make my own. I made a int vector class and it works fine, but when trying to make it for strings it compiles but gives me an error because of the pointers. Any hint where I am making the mistake? All I did was change every int element for string, but aparently that does not work. Please help I am very confused.
public:
StringRow(){
elements = new string;
size = 0;
}
~StringRow(){...}
void push_back(string value){...}
};
You defined pointer to variable, not array of variables.
elements = new string;
Replace it with
elements = new string[size];
You can optimize algorithm with defining initial size. Create bigger array only if it's necessary.
There are several problems:
in the constructor you don't need to allocate anything. You don't even need a constructor here, you can initialize the members directly as you declare them.
if you allocate with string* tmpElementsArray = new string[size + 1]; you need to deallocate with delete [] tmpElementsArray;
Corrected working version:
#include <string>
#include <iostream>
using namespace std;
class StringRow {
private:
string* elements = nullptr;
int size = 0;
public:
// constructor not needed
// StringRow() {
// elements = nullptr;
// size = 0;
// }
~StringRow() {
delete []elements;
}
void push_back(string value) {
string* tmpElementsArray = new string[size + 1];
for (int i = 0; i<size; i++) {
tmpElementsArray[i] = elements[i];
}
delete [] elements;
elements = tmpElementsArray;
elements[size] = value;
size++;
}
int length() {
return size;
}
string at(int index) {
if (index<size) {
return elements[index];
}
}
};
int main()
{
StringRow s;
string str1 = "hello";
string str2 = "hello2";
s.push_back(str1);
s.push_back(str2);
cout << s.at(0) << endl ;
cout << s.at(1) << endl;
}
Doing a delete []elements if elements is nullptr is OK.
NB: This is not the most efficient way.
This is my code i have this error.I think it is a problem with my destructor.
Could you explain me this?
Destructor causes corruption of the heap
#pragma once
#include<iostream>
class Adresa
{
char *nume;
int numar;
char *localitate;
public:
Adresa();
Adresa(char *n, int nr, char*l);
Adresa(Adresa &adr);
void print()
{
printf("%s %d %s ", nume, numar, localitate);
}
~Adresa();
};
and
#include "Adresa.h"
#include<iostream>
Adresa::Adresa()
{
nume = new char();
localitate = new char();
numar = NULL;
}
Adresa::Adresa(char *n, int nr, char*l)
{
this->nume = _strdup(n);
this->localitate = _strdup(l);
this->numar = nr;
}
Adresa::Adresa(Adresa &adr)
{
this->nume = new char(strlen(adr.nume) + 1);
strcpy(nume, adr.nume);
this->numar = adr.numar;
this->localitate = new char(strlen(adr.localitate) + 1);
strcpy(localitate, adr.localitate);
}
Adresa::~Adresa()
{
if (nume != NULL)
{
delete[] nume;
}
if (localitate != NULL)
{
delete[] localitate;
}
}
and the output is this
enter image description here
What is the problem with my destructor?
You had mixed up java and C++. In new expression
what goes into parents AFTER type-id isn't the size of allocated block, it's a part of initializer.
char *s = new char(3);
allocates one character and assigns value of 3 to it.
What you had to write
char *s = new char[3];
only in this case it's legal to use delete[]
You can't use normal delete on such pointer. Well, you can but usually you should not, because it leads to undefined behaviour, e.g. will not deallocate whole array on some platforms.
Basically, I'm passing a pointer to a character string into my constructor, which in turn initializes its base constructor when passing the string value in. For some reason strlen() is not working, so it does not go into the right if statement. I have checked to make sure that there is a value in the variable and there is.
Here is my code, I've taken out all the irrelevant parts:
Label class contents:
Label(int row, int column, const char *s, int length = 0) : LField(row, column, length, s, false)
{
}
Label (const Label &obj) : LField(obj)\
{
}
~Label()
{
}
Field *clone() const
{
return new Label(*this);
}
LField class contents:
LField(int rowNumVal, int colNumVal, int widthVal, const char *valVal = "", bool canEditVal = true)
{
if(strlen(valVal) > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Field *clone() const
{
return new LField(*this);
}
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
strcpy(val, clone.val);
rowNum = clone.rowNum;
colNum = clone.colNum;
width = clone.width;
canEdit = clone.canEdit;
index = clone.index;
}
Screen class contents:
class Screen {
Field *fields[50];
int numOfFields;
int currentField;
public:
Screen()
{
numOfFields = 0;
currentField = 0;
for(int i = 0; i < 50; i++)
fields[i] = NULL;
}
~Screen()
{
for (int i = 0; i < 50; i++)
delete[] fields[i];
}
int add(const Field &obj)
{
int returnVal = 0;
if (currentField < 50)
{
delete[] fields[currentField];
fields[currentField] = obj.clone();
numOfFields += 1;
currentField += 1;
returnVal = numOfFields;
}
return returnVal;
}
Screen& operator+=(const Field &obj)
{
int temp = 0;
temp = add(obj);
return *this;
}
};
Main:
int main () {
Screen s1;
s1 += Label(3, 3, "SFields:");
}
Hopefully someone is able to see if I am doing something wrong.
<LANGUAGE FEATURE XXXX IS BROKEN>! ... No, it isn't.
Just before measuring the string, write in a puts(valVal), to ensure you are not mistaken about the contents of that variable.
Marcin at this point the problem will come down to debugging, I copied your code with some minor omissions and got the correct result.
Now it needs to be said, you should be using more C++ idiomatic code. For instance you should be using std::string instead of const char* and std::vector instead of your raw arrays.
Here is an example of what the LField constructor would look like with std::string:
#include <string> // header for string
LField(int rowNumVal,
int colNumVal,
int widthVal,
const std::string& valVal = "",
bool canEditVal = true)
{
std::cout << valVal;
if(valVal.length() > 0)
{
}
else
{
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
//val = NULL;
}
}
Using these types will make your life considerably easier and if you make the change it may just fix your problem too.
PREVIOUS:
So you can be CERTAIN that the string is not being passed in correctly add a printline just before the strlen call. Once you do this work backward with printlines until you find where the string is not being set. This is a basic debugging technique.
Label(int row,
int column,
const char *s,
int length = 0) :
LField(row, column, length, s, false) {
}
LField(int rowNumVal,
int colNumVal,
int widthVal,
const char *valVal = "",
bool canEditVal = true)
{
std::cout << valVal << std::endl;
if(strlen(valVal) > 0)
{
}
else {
//This is where it jumps to, even though the value in
//valVal is 'SFields:'
val = NULL;
}
}
Whenever there is strange behavior like this, memory getting screwed up is almost always the culprit. Never mix new with delete[] OR new[] with delete. The latter is slightly worse than the former but they are both bad news. delete[] should only be used in conjunction with new[]. Mixing these allocation/deallocation notations will result in undefined behavior. Since you are never using new[], replace all of your delete[] calls with delete. It is also good practice and good manners to set your pointers to NULL after you delete them. It is highly unlikely that you will be the only one debugging this code and it would be extremely annoying to debug your pointers thinking that there is valid memory there, when in fact there isn't.
Mixing these notations inevitably lead to exploits and memory leaks.
There is a problem here:
LField(const LField &clone) {
delete[] val;
val = new char[strlen(clone.val) + 1];
val is uninitialized when the constructor is called, and you are deleting it.