How to use properly external pointers for allocating memory in constructors and deallocating in destructors in C++? Following is a code example that is not working properly. Please consider it.
#include <iostream>
using namespace std;
class Foo
{
public:
Foo() : str(nullptr)
{
str = new string();
}
~Foo()
{
if (str != nullptr)
{
delete str;
str = nullptr;
}
}
void getString(string ** pStr)
{
*pStr = str;
}
private:
string * str;
};
int main()
{
string * mainStr = nullptr;
Foo().getString(&mainStr);
if (mainStr == nullptr)
cout << "is null";
else
cout << "is not null";
return 0;
}
How to write above code in order to mainStr variable has the correct value (nullptr in this case)?
In C++, you will need an instance of an object in order to use it's non-static member functions.
In your case:
int main(void)
{
Foo my_foo;
std::string * p_string = nullptr;
my_foo.getString(&p_string);
if (mainStr == nullptr)
cout << "is null";
else
cout << "is not null";
return 0;
}
You will need to test pStr in getString for null, as assigning to a null pointer is undefined behavior.
Related
Well, i implemented a FIFO stack (push,pop) stack in a Class with mem allocated ints.
So i asked to myself: if "int" is a datatype, why i cannot push "ADTs" to my own stack.
Then i came with this code:
#include <iostream>
class Person {
std::string name;
int age;
public:
Person(std::string pName = "", int pAge = 1)
{
name = pName;
age = pAge;
}
void Print()
{
std::cout << name << " " << age << std::endl;
}
};
class Stack {
Person * stack;
int size, top;
int index;
public:
Stack(int stackSize)
{
top = stackSize -1;
index = top;
stack = new Person[stackSize];
}
void push(Person person)
{
if (index < 0)
std::cout << "Stack UNDERFLOW" << "Index is: " << index << std::endl;
stack[index--] = person;
}
Person & pop()
{
if (index > top)
{
std::cout << "Stack OVERFLOW" << std::endl;
}
return stack[++index];
}
};
I know, there are stacks, queues, vectos, etc in the STL lib. I just wanted to do it by myself.
I want the stack push a copy of the object.
I'm not sure i don't know if the compiler is pushing addresses, copying the whole object (what is what i want) or what.
Please enlight me.
Here is my main() code:
int main()
{
Stack stack(100);
Person person("Lucas", 39);
for (int i = 0; i < 100; i++)
{
stack.push(person);
((Person)stack.pop()).Print();
}
return EXIT_SUCCESS;
}
To answer your question about copies, this:
stack[index--] = person;
makes a copy, because the type on both sides of the assignment is of type T.
This:
stack.push(person);
also makes a copy, because you are passing person by value. To avoid this (redundant) copy, declare push as:
void push(const T &person)
Well, what i did to solve my question was what #PaulMcKenzie said in his comment.
I created a template for the "Stack" class and any template "T" is the datatype that is passed to the this class.
About the main methond (in my question) it had an unnecesary cast to (Person) since it's implied.
Edit 2:
#Paul Sanders were right too, i was doing a redundant copy at push()
This way i solved my problem:
#include <iostream>
class Person {
std::string name;
int age;
public:
Person(std::string pName = "", int pAge = 1)
{
name = pName;
age = pAge;
}
void Print()
{
std::cout << name << " " << age << std::endl;
}
};
template <class T>
class Stack {
T * stack;
int size, top;
int index;
public:
Stack(int stackSize)
{
top = stackSize -1;
index = top;
stack = new T[stackSize];
}
void push(const T &person)
{
if (index < 0)
std::cout << "Stack UNDERFLOW" << std::endl;
stack[index--] = person;
}
T pop()
{
if (index > top)
{
std::cout << "Stack OVERFLOW" << std::endl;
}
return stack[++index];
}
};
int main()
{
Stack<Person> stack(100);
Person person1("Lucas", 39);
Person person2("Gustavo", 38);
for (int i = 0; i < 100; i++)
{
if (i % 2 == 0)
stack.push(person1);
else
stack.push(person2);
}
for (int i = 0; i < 100; i++)
stack.pop().Print();
return EXIT_SUCCESS;
}
In the example of the main() function, it creates an stack of Person objects of 100. Then i create 2 people: "Lucas" and "Gustavo" and push it intercalated to the my stack for 100 times (the first for statment).
Then the second and final for statement pop() all values and print them.
I want to pass a double pointer as argument to a function, but I cant see what I am doing wrong.
#include <iostream>
#include <string>
using namespace std;
void changeString(string ***strPtr) {
strPtr = new string**[1];
*strPtr = new string*[1];
**strPtr = new string("hello");
//cout << strPtr[0][0][0];
}
int main()
{
string **strPtr;
changeString(&strPtr);
//cout << strPtr[0][0];
return 0;
}
The cout in changeString works fine, but the cout in main throws the exception read access violation. strPtr was 0xCCCCCCCC.
Your example is basically equivalent to this:
void changeString(string **strPtr) {
strPtr = new string*[1];
*strPtr = new string("hello");
//cout << strPtr[0][0];
}
int main()
{
string *strPtr;
changeString(&strPtr);
//cout << strPtr[0];
return 0;
}
And that is basically equivalent to this:
void changeString(string *strPtr)
{
strPtr = new string("hello"); // Changes the pointer, not the string!
//cout << strPtr[0];
}
int main()
{
string str;
changeString(&str);
//cout << str;
return 0;
}
At this point it should start to become obvious that you are assigning a new value to the pointer, not the pointed-to object. Consider this:
void change(SomeType t)
{
t = somethingElse;
}
int main()
{
SomeType t;
change(t); // Does not change t.
}
In your case, SomeType happens to be string* (or string***) - you are just overwriting a local variable.
To fix your code, just skip the first line in changeString:
http://coliru.stacked-crooked.com/a/88874ee3601ef853
void changeString(string ***strPtr)
{
*strPtr = new string*[1];
**strPtr = new string("hello");
cout << strPtr[0][0][0];
}
int main()
{
string **strPtr;
changeString(&strPtr);
cout << strPtr[0][0];
return 0;
}
I have a class foo like this:
class foo
{
private:
int* a;
public:
foo()
{
a = new int[4];
cout << "a" << endl;
}
};
When I create new object named foo1 and then I debug, after the allocating line, it yields the result: a 0x005a4580 {-842150451}.
But when I replace all int-s by char-s in class definition, it yields an undesired result:
a 0x005694a0 "ÍÍÍÍýýýý\x6ŒÒ•\x5Ÿ"
that the size of a is now greater than 4.
I dont know what happened. Could you please give me an explanation?
Full code:
#include <iostream>
#include <string>
using namespace std;
class String
{
public:
String(char* data)
{
setSize(0);
while (*(data + size) != '\0')
size++;
this->data = new char[size];
//need to allocate memory for 'data' pointer because 'data' pointer is now on the stack and the data must be on the heap
memcpy(this->data, data, size * sizeof(char));
}
void operator=(String rhs)
{
if (this->data != NULL)
delete[] this->data, data = NULL;
this->data = new char[rhs.getSize()]; //allocate
memcpy(this->data, data, size * sizeof(char));
}
int getSize()
{
setSize(0);
while (*(data + size))
size++;
return size;
}
void setSize(int size)
{
this->size = size;
}
void display()
{
for (int i = 0; i < size; i++)
cout << *(data + i);
}
~String()
{
if (data != NULL)
delete[] data, data = NULL;
}
private:
char* data;
int size;
};
void main()
{
String a("abcd");
String b("1");
a.display();
cout << endl;
cout << b.getSize() << endl;
a = b;
cout << a.getSize() << endl;
system("pause");
}
Whatever you're using to look at a doesn't know how much you allocated. It just knows the type.
In the first version it sees int *, so it shows a single int.
In the second version it sees char *, so it assumes it's a C string and prints whatever is in memory up to the first '\0' byte.
The cplusplus.com documentation on getenv() states...
The pointer returned points to an internal memory block, whose content or validity may be altered by further calls to getenv
...which I take to mean, "If you want to keep the content, copy it." So, since I need to retrieve several variables, I wrote a couple of little wrapper functions:
#include <iostream>
#include <string.h>
using namespace std;
void getEnv (char *val, const char *var) {
val = nullptr;
char *enVar = getenv(var);
if (enVar != nullptr) {
val = new char[strlen(enVar) + 1];
strcpy(val, enVar);
}
}
void getEnv (int &val, const char *var) {
val = -1;
char *enVar = getenv(var);
if (enVar != nullptr) {
val = atoi(enVar);
}
}
int main() {
char *textMode = nullptr;
int cLen = 0;
getEnv(cLen, "CONTENT_LENGTH");
cout << cLen << endl << endl;
getEnv(textMode, "TEXT_MODE");
if (textMode == nullptr)
cout << "Not set.";
else
cout << "[" << textMode << "]<br>\n";
return 0;
}
The int version works as expected, but I get nothing back from the char version, and I mean nothing: if I don't initialize *textMode at declaration it remains an uninitialized pointer.
It's pointers, right? Right? I know it is. Gotta be pointers. I'll figure them out one of these days, but hey -- at least I got my linked list to work! Yay!
Your second function takes val (an int) by reference: void getEnv (int &val, const char *var) and so can modify the variable passed to it as you expect.
Your first function takes val (a char*) by value: void getEnv (char *val, const char *var) so modifying val has no affect on the variable passed to it. A simple solution is to simply take it as a reference as well: void getEnv (char *&val, const char *var)
Follow up to my comments and the OP's response to them.
Here's what I was thinking:
#include <iostream>
#include <string.h>
using namespace std;
// Use a class to encapsulate the data need to be captured
// in an environment variable.
class EnvironmentVariable
{
public:
EnvironmentVariable(char const* name) : name_(name), isSet_(false)
{
char *val = getenv(name);
if ( val != nullptr )
{
isSet_ = true;
this->value_ = val;
}
}
bool isSet() const
{
return isSet_;
}
void getValue(char const*& val) const
{
if ( isSet_ )
{
val = this->value_.c_str();
}
else
{
val = nullptr;
}
}
void getValue(int& val) const
{
if ( isSet_ )
{
val = stoi(this->value_);
}
else
{
val = 0; // Find a suitable default value
}
}
private:
std::string name_;
std::string value_;
bool isSet_;
};
int main() {
char const* textMode = nullptr;
int cLen = 0;
EnvironmentVariable env1("CONTENT_LENGTH");
env1.getValue(cLen);
cout << cLen << endl << endl;
EnvironmentVariable env2("TEXT_MODE");
env2.getValue(textMode);
if (textMode == nullptr)
cout << "Not set.\n";
else
cout << "[" << textMode << "]<br>\n";
return 0;
}
I'd like to know why the following program gets the error "double free or corruption (fasttop)" when I run the program. I know I can use string instead of character array. But I'd like to use character array with dynamic memory allocation. Could you please let me know how I can fix this problem?
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
class Cube
{
public:
char *str;
Cube(int len)
{
str = new char[len+1];
}
Cube(const Cube &c)
{
str = new char[strlen(c.str) + 1];
strcpy(str, c.str);
}
~Cube()
{
delete [] str;
}
};
int main()
{
vector <Cube> vec;
for (int i = 0; i < 10; i++)
{
char in [] = "hello !!";
Cube c(strlen(in)+1);
strcpy(c.str, in);
vec.push_back(c);
}
int i = 0;
for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); )
{
cout << it->str << endl;
i++;
if (i % 2 == 0)
it = vec.erase(it);
else
it++;
}
for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); it++)
{
cout << it->str << endl;
}
return 0;
}
You forgot to define operator= for your class. This is the rule of Big Three (copy ctor, dtor, assignment must all be defined).
n.m. has already given a fine answer, but I found this question interesting so I decided to try to understand it a little better.
It turns out then when you call erase() on the first item of an iterator (which we will call item0), here's what the iterator does: it uses the = operator of your class to do item0 = item1. Then it deletes item1.
If you don't define your own = operator, I think it will simply copy the memory of your object over from item1 to item0, so item0 and item1 will temporarily be pointing to the same string. Then when item1 gets deleted, the string gets freed, leaving item0 in an invalid state because it has a pointer to memory that has been freed.
Here is some simple test code that reproduces and illuminates the problem:
#include <cstring>
#include <vector>
#include <stdio.h>
using namespace std;
class Cube
{
public:
char * str;
Cube(const Cube &c) { set(c.str); }
Cube(const char * s) { set(s); }
~Cube() { clear(); } // is "delete []" necessary? not sure
#if 1 // change to 0 to cause a bug
void operator=(const Cube &c)
{
clear(); // necessary to avoid memory leaks
printf("operator=\n");
set(c.str);
}
#endif
private:
void set(const char * s)
{
str = new char[strlen(s) + 1];
printf("allocated %p for %s\n", str, s);
strcpy(str, s);
}
void clear()
{
if (str)
{
printf("freeing %p: %s\n", str, str);
delete str;
}
}
};
int main(int argc, char ** argv)
{
printf("== CREATING VECTOR ==\n");
vector <Cube> vec;
vec.push_back(Cube("octopus"));
vec.push_back(Cube("squid"));
printf("== BEGINNING ITERATION ==\n");
vector<Cube>::iterator it = vec.begin();
printf("First entry is %p %s\n", it->str, it->str);
it = vec.erase(it);
printf("Second entry is %p %s\n", it->str, it->str); // this prints garbage if Cube has no = operator
return 0;
}
This code produces the following output:
== CREATING VECTOR ==
allocated 00350F98 for octopus
allocated 00350FB8 for octopus
freeing 00350F98: octopus
allocated 00350F98 for squid
allocated 00350FD8 for squid
allocated 00350FE8 for octopus
freeing 00350FB8: octopus
freeing 00350F98: squid
== BEGINNING ITERATION ==
First entry is 00350FE8 octopus
freeing 00350FE8: octopus
operator=
allocated 00350F98 for squid
freeing 00350FD8: squid
Second entry is 00350F98 squid
freeing 00350F98: squid
I compiled and ran this in Windows with MinGW. The command I used was g++ -Wl,--enable-auto-import test.cpp && a.exe.
If it hurts, don't do it:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Cube
{
public:
string str;
Cube(const string& s) : str(s) { }
};
int main()
{
vector <Cube> vec;
for (int i = 0; i < 10; i++)
{
char in [] = "hello !!";
vec.push_back(Cube(in));
}
int i = 0;
for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); )
{
cout << it->str << endl;
i++;
if (i % 2 == 0)
it = vec.erase(it);
else
it++;
}
for ( vector<Cube>::iterator it = vec.begin(); it < vec.end(); it++)
{
cout << it->str << endl;
}
return 0;
}
Happens to be shorter and correct (not tested).