I have a class IStream2:
class IStream2 {
private:
char* fn;
public:
IStream2(char* filename);
char* get_filename();
};
IStream2::IStream2(char *filename) {
strcpy(fn, filename);
}
char * IStream2::get_filename() {
return fn;
}
And here is the main code:
vector<IStream2> istreams;
char fn[] = "file1.txt";
IStream2 reader2 = IStream2(fn);
istreams.push_back(reader2);
char fn2[] = "file2.txt";
IStream2 reader3 = IStream2(fn2);
istreams.push_back(reader3);
cout << istreams[0].get_filename() << endl;
It prints file2.txt but I expected file1.txt.
I know that I should use string but I would like to resolve this problem.
IStream2::IStream2(char *filename) {
strcpy(fn, filename);
}
Allocates no storage for fn. strcpy(fn, filename); invokes undefined behaviour writing into whatever storage fn points at, and after that all bets are off. The program could do anything.
The right answer is to use std::string
class IStream2 {
private:
std::string fn;
public:
IStream2(const char* filename); // note const. if not modifying a passed rference,
// mark it const. The compiler can make optimizations
// and can catch mistakes for you
// also the function can now receive string literals
const char* get_filename(); // another const this is because a string won't
// easily give you a non const pointer
}; <-- note the added ;
IStream2::IStream2(const char *filename): fn(filename) {
}
const char * IStream2::get_filename() {
return fn.c_str(); // get the char array from the string
}
But I suspect this is an exercise in writing C with Classes, so back into the stone ages we go. This is a LOT more work because we have to manage all of the memory ourselves. For example, We need to observe the Rule of Three. What is The Rule of Three?
Sigh.
class IStream2 {
private:
char* fn;
public:
IStream2(const char* filename); // note const char *
~IStream2(); // need destructor to clean up fn. This means we need to
// comply with the Rule of Three
IStream2(const IStream2 & src); // copy constructor
IStream2 & operator=(IStream2 src); // assignment operator
char* get_filename(); // Note: by returning a non const pointer here we
// allow the caller to tamper with the contents of
// fn and even delete it. This defeats the point
// of declaring fn private, so avoid doing this.
};
IStream2::IStream2(const char *filename) {
fn = new char[strlen(filename) +1]; // allocate storage.
// The +1 is to hold the string's NULL terminator
strcpy(fn, filename);
}
// implement copy constructor
IStream2::IStream2(const IStream2 & src) {
fn = new char[strlen(src.fn) +1];
strcpy(fn, src.fn);
}
// implement destructor
IStream2::~IStream2()
{
delete[] fn;
}
// implement assignment operator. Using Copy And Swap Idiom
IStream2 & IStream2::operator=(IStream2 src)
{
std::swap(fn, src.fn);
return *this;
}
char * IStream2::get_filename() {
return fn;
}
int main()
{
vector<IStream2> istreams;
const char* fn = "file1.txt"; // note const char *. string literals may be
// read-only memory and can't be changed
IStream2 reader2 = IStream2(fn);
istreams.push_back(reader2);
const char* fn2 = "file2.txt"; // and again const char *
IStream2 reader3 = IStream2(fn2);
istreams.push_back(reader3);
cout << istreams[0].get_filename() << endl;
return 0;
}
Since we are wresting with dinosaurs, I won't bother with the Rule of Five and move operations, but see how much more annoying it is to have to do this the wrong way?
More on Copy And Swap Idiom
Related
When compiling this code, I get this warning:
Conditional jump or move depends on uninitialized value(s)
My set function is using a dynamic array and I wish to have it delete the dynamic space if it already exists.
The class:
class Name
{
char* m_name;
public:
Name();
Name(const char*);
~Name();
void set(const char*);
}
The constructor and setEmpty():
void Name::setEmpty()
{
m_name = nullptr;
}
Name::Name()
{
setEmpty();
}
The function:
void Name::set(const char* name)
{
if (name == nullptr || std::strlen(name) == 0)
{
setEmpty(); // <-- What my constructor has; sets 'm_name' to nullptr;
}
else
{
int length = std::strlen(name) + 1;
if (m_name != nullptr) // <-- gives warning because m_name is not initialized however it was created via constructor (?)
{
delete[] m_name;
}
m_name = new char[length]; // <-- No matter what I want to re-create the dynamic array
std::strcpy(m_name, name);
}
}
Main():
int main()
{
// constructors
Name s1("Sample");
Name s2, s3;
Name badData[] = {
Name ("Kappa"),
Name("Omega"),
Name(nullptr),
Name("", )
};
s1.set("Sample");
//...//
}
It's not terribly clear, but if your class has any constructors that don't call setEmpty(), possibly one of them does not initialise m_name. If that happens, a subsequent call of set() with a non-null pointer for which strlen() returns a non-zero value will test the value of m_name when it is uninitialised. You haven't shown how the constructor that accepts a const char * is defined, so it is possible that constructor is the culprit. – Peter 5 mins ago
This actually was the problem.
Solution:
Name::Name(const char* name)
{
setEmpty(); // <-- call safe state in all constructors
set(name, dob, power, level, super);
}
I didn't realize I needed to call either the function or the constructor that initializes values before I use my set function in the overloaded constructor.
Thank you Peter.
I have 10 char* properties of my class called Car,what is the best way to write the setters of these 10 char* properties? One way is to directly set the value in it :
void Car<T>::setKey(const char* toCopyKey)
{
delete[] key;
int length=strlen(toCopyKey);
key=new char[length+1];
strcpy(key,toCopyKey);
}
and do this 10 times ,other solution I thought of , is to make a function that creates a copy of the passed char* and then assigns it in the setter :
char* Car<T>::copyString(const char* s)
{
int length=strlen(s);
char* property=new char[length+1];
strcpy(property,s);
return property;
}
and use the copyString method in every setter like this :
void Car<T>::setModel(const char* toCopyModel)
{
delete[] model;
model=copyString(toCopyModel);
}
But I was wondering if this second solution is correct and if there is a better way to do this copying?I cannot use std::string and vector.
I guess this is an assignment of some C++ course or tutorial, because otherwise I would recommend to question the whole design.
In general, I would learn as early as possible to not do manual memory management at all and use C++ standard library smart pointers. This relieves you from the burden to write destructors, copy|move-assignment and copy|move constructors.
In your example, you could use std::unique_ptr<char[]> to hold the string data. This is also exception safe and prevents memory leaks. Creation of the unique_ptr<char[]> objects can be centralized in a helper method.
class Car {
private:
std::unique_ptr<char[]> model;
std::unique_ptr<char[]> key;
static std::unique_ptr<char[]> copyString(char const* prop) {
auto const len = std::strlen(prop);
auto p = std::make_unique<char[]>(len+1);
std::copy(prop, prop + len, p.get());
p[len] = '\0';
return p;
}
public:
void setModel(char const* newModel) {
model = copyString(newModel);
}
void setKey(char const* k) {
key = copyString(k);
}
char const* getModel() const {
return model.get();
}
};
If you don't know them, I would recommend to read about the rule of zero.
You can combine your two methods by using a reference parameter:
static void Car<T>::setStringProp(char *&prop, const char *toCopyString) {
delete[] prop;
prop = new char[strlen(toCopyString)+1];
strcpy(prop, toCopyString);
}
void Car<T>::setModel(const char *toCopyModel) {
setStringProp(model, toCopyModel);
}
And make sure that your constructor initializes all the properties to NULL before calling the setters, because delete[] prop requires that it be an initialized pointer.
Car<T>::Car<T>(const char *model, const char *key, ...): model(nullptr), key(nullptr), ... {
setModel(model);
setKey(key);
...
}
I have implemented a class string, similar to std::string one.
I have a problem when the destructor is called: the field length has the length of the characters allocated in field.
This is the class:
class indexException:public std::exception
{
public:
virtual const char* what()
{
return "Index is either too long, or negative";
}
};
class string
{
public:
static const unsigned int length_max=100;
string(const char* field=NULL)
{
if(field!=NULL)
{
const unsigned int length=strlen(field);
this->field=new char[length+1];
this->length=length;
for(unsigned int i=0;i<=length;i++)
this->field[i]=field[i];
}
else
{
this->field=NULL;
length=0;
}
}
string(string& str)
{
string(str.field);
}
~string()
{
if(length>0)
delete field;
}
char& operator[] (int i) const throw()
{
try
{
if(i<0 || i>=(int)length)
throw indexException();
}
catch(indexException& e)
{
std::cerr << e.what() << std::endl;
}
return field[i];
}
string& operator=(const char* field)
{
const unsigned int length=strlen(field);
if(this->length>0)
delete this->field;
this->field=new char[length];
this->length=length;
for(unsigned int i=0;i<length;i++)
this->field[i]=field[i];
return *this;
}
string& operator= (const string& str)
{
if(this!=&str)
*this=str.field;
return *this;
}
operator char* ()
{
return field;
}
friend std::ostream& operator<< (std::ostream& out, string& str);
friend std::istream& operator>> (std::istream& in, string& str);
public:
unsigned int length;
char* field;
};
std::ostream& operator<<(std::ostream& out, string& str)
{
out << str.field;
return out;
}
std::istream& operator>> (std::istream& in, string& str)
{
char temp[string::length_max];
in >> temp;
str=temp;
return in;
}
If I use the assignment operator, this doesn't cause a segmentation fault.
But it undirectly cause it.
I explain how:
int main(int argc,char** argv)
{
string str="hi";
string str2=str;
return 0;
}
Putting a breakpoint into the assignment operator overloading, I realized that the assigment operator doesn't cause segmentation fault.
The problem is after, when exiting from main.
If I remove the destructor I don't get this segmentation fault, but I would know why I get this problem.
Edit: I have understood where's the problem.
I followed your suggestions but it still goes to segmentation fault.
But now it doesn't crash anymore on the destructor method, but on the assignment operator overloading:
string& operator=(const char* field)
{
unsigned int length=0;
if(field!=NULL)
length=strlen(field);
else
field="";
if(this->length>0)
delete[] this->field;
this->field=new char[length+1];
this->length=length;
strcpy(this->field,field);
return *this;
}
The problem is when I delete this->field, the debugger stops there.
An example of segmentation fault:
string str("hi");
string str2=str;
This causes segmentation fault.I suppone it's because str2 is not initialized, and length has an undefined value.
If I instead do this:
string str("hi");
string str2;
str2=str;
There isn't any segmentation fault.Why?
I thought that calling :
string str2;
Was also calling the constructor, or is that the "=" operator has the precedence?
How to solve this?
PS: I also changed other things,like the copy constructor.
Full code is here:
http://pastebin.com/ubRgaVr8
Solved: I changed the copy constructor as suggested in the accepted reply:
string(const string& str)
{
length=str.length;
field=new char[str.length+1];
memcpy(field,str.field,length+1);
}
Your copy constructor doesn't initialise the object.
string(string& str)
{
string(str.field); // Does nothing
}
string(str.field)creates an unnamed stringand immediately throws it away.
It does not initialise this object using a different constructor.
Since your object now consists only of randomness, bad things will happen when you try to destroy it.
To make sure things are initialised, make a private member function
void initializeFromChars(const char* cString);
that does the work and use it in your constructors and assignment operator.
EDIT: Scrapped my previous answer, as it was incorrect.
The problem appears to be the copy constructor, you are passing the field from the source instance as though it is merely another null terminated char*, but it isn't.
You don't copy the null character at the end during the char* assignment invoked by the previous statement, you use an internal length field instead, and copy only that many bytes.
so your copy constructor should be:
string(string& str)
{
length = str.length;
field = new char[length];
memcpy(field, str.field, length);
}
or, if you want to preserve compatibility with null terminated functions, and you have ensured that the null is kept for all other assignments/constructors, etc:
string(string& str)
{
length = str.length;
field = new char[length + 1];
memcpy(field, str.field, length + 1);
}
In fact, the mixing null terminated, and specified length strings so much throughout your class appears to be confusing you.
I would create an internal, private, single disposal method, and an array of methods to set various source types, and have the constructors, assignment operators, and destructors use those instead.
That way you only have a single places where any given operation occurs, rather than juggling many minor variations on the same functionality. For example:
private:
void internalSet(const char *source) {
if (source == NULL) {
length = 0;
field = NULL;
}else{
length = strlen(source);
field = new char[length];
memcpy(field, source, length);
}
}
void internalSet(const string &source) {
length = source.length;
if (length > 0) {
field = new char[length];
memcpy(field, source.field, length);
}else{
field = NULL;
}
}
void internalDispose() {
delete[] field;
}
public:
string() : field(NULL), length(0) {}
string(const string& source) { internalSet(source); }
string(const char *source) { internalSet(source); }
~string() { internalDispose(); }
string& operator=(const char *source) {
internalDispose();
internalSet(source);
return *this;
}
string& operator=(const string &source) {
internalDispose();
internalSet(source);
return *this;
}
void clear() {
internalDispose();
length = 0;
}
Your destructor uses delete, when it should use delete[].
Once you allocate memory with
field = new char[length+1];
You should delete it with:
delete [] field;
And you're not checking whether your allocation was successful.
Another thing considered good practice is setting field to NULL after delete so it won't get deleted twice (if you start delivering classes) for example:
~string(){
delete [] field;
// field = NULL;
}
Note: according to Dietmar Kühl setting field=NULL isn't good practice (take a look at the comments) and choose your way, here's question specifically about this: Is it worth setting pointers to NULL in a destructor? .
Note 2: KerrekSB pointed out that delete [] field will do nothing if pointer is NULL and whole condition is unnecessary.
Than in string& operator=(const char* field) you probably want to allocate length + 1 and iterate to it too (to include terminating NULL).
And I don't like your string& operator= (const string& str), you have cached info on length of string and you're using strlen() and than manual copy char by char.
Your copy constructor also looks bad... You should "copy" manual allocation and copy byte by byte to it. Or rather build protected function like fromCString(const char *) and use it in both constructors and assign operators.
If those doesn't help ask in comment for more help.
Part of a larger program I am making requires a path to be read in from the command line and stored in a class. Because paths can be an arbitrary size and it's needed in multiple functions, I store it in a char* in the header file. But, for some reason, when I assign a value to it, the program segfaults.
The debugger (gdb) shows the following:
Program received signal SIGSEGV, Segmentation fault.
0x00007ffff7b4828a in std::basic_istream<char, std::char_traits<char> >& std::operator>><char, std::char_traits<char> >(std::basic_istream<char, std::char_traits<char> >&, char*) ()
from /usr/lib/libstdc++.so.6
Here's the program I wrote to demonstrate the problem:
test.cpp:
#include "test.h"
#include <iostream>
#include <cstring>
Test::Test() {
filepath = NULL;
}
void Test::set_path(char* string) {
char temp[strlen(string) + 1];
strcpy(filepath, temp);
}
char * Test::get_path() {
return filepath;
}
int main(int argc, char *argv[]) {
std::cout << "Enter a file path: ";
char *temp;
std::cin >> temp;
Test *temp2 = new Test();
temp2->set_path(temp);
std::cout << "Path: " << temp2->get_path() << std::endl;
}
test.h:
#ifndef TEST_H
#define TEST_H
class Test {
private:
char *filepath;
public:
Test();
void set_path(char *);
char * get_path();
};
#endif // TEST_H
I'm not sure why it crashes. Is there something wrong with the method I'm doing this? Also, rather than just switching to strings, I'd like to find out more about this problem.
Thanks in advance!
temp (inside main) is uninitialized and not pointing to any valid allocated block of memory, therefore the line:
std::cin >> temp;
is causing input to be written into some unknown part of memory, causing undefined behaviour. You should either:
Make temp a char[] and only read in an amount of characters that will fit in the buffer.
Point temp to a valid buffer.
Better yet, make temp an std::string, and let the std::string class worry about the memory management.
You're also going to be having a similar problem with filePath after you fix the above problem. filePath is being set to NULL in the Test constructor, and then you're copying temp to the block of memory pointed by filePath in Test::set_path:
strcpy(filepath, temp);
NULL refers to an address that you're not allowed to dereference. You should change all of your C-strings to std::strings and use the std::string member functions and overloaded operators to deal with strings in C++.
You call strcpy without allocating memory for the string to be copied in set_path. The manual page clearly states that dest must be large enough to contain the string in src. Besides you copy from an empty temporary. Although, your crash appears early, when you read from cin into an uninitialized pointer.
Use std::string.
#include <iostream>
class Foo
{
public:
Foo(const std::string& s) : s_(s) {} ;
private:
std::string s_;
};
int main()
{
std::string f;
std::cin >> f;
std::cout << f << std::endl;
Foo foo = Foo(f);
return 0;
}
If you really preffer what you are doing:
class Foo
{
public:
Foo(const char* s) {
size_t size = strlen(s);
s_ = new char[size + 1];
// safer
strncpy(s_, s, size + 1);
} ;
private:
char* s_;
};
int main()
{
char buffer[1024];
// be save
buffer[1023] = '\0';
Foo foo = Foo(buffer);
return 0;
}
The second example is still broken. It is missing a proper destructor, copy constructor and assignment operator. I'll leave this as an exercise.
Test::Test() {
filepath = NULL;
}
void Test::set_path(char* string) {
char temp[strlen(string) + 1];
strcpy(filepath, temp);
}
I'm not sure what you thought that strcpy was going to do, but what it does is copy from temp, which is uninitialized, to filepath, which is NULL. So neither parameter makes any sense.
I have this piece of code that prints the content of a directory using Boost.Filesystem:
class Shell {
private:
const string& command;
const path& firstPath;
const path& secondPath;
public:
// constructor
Shell(const string& _command, const path& _firstPath, const path& _secondPath = path()): command(_command), firstPath(_firstPath), secondPath(_secondPath) {}
//destructor
~Shell() {}
//execute commands
void executeCommand()
{
if(command.compare("ls"))
{
ls();
}
}
void ls()
{
if(exists(firstPath))
{
vector<path> vecPath;
copy(directory_iterator(firstPath), directory_iterator(), back_inserter(vecPath));
sort(vecPath.begin(), vecPath.end());
for(vector<path>::iterator it = vecPath.begin(); it != vecPath.end(); ++it)
{
cout << *it << endl;
}
}
}
};
int main(int argc, char** argv)
{
path Path = "c:\\workspace";
Shell shell("ls", Path); // GOOD
// Shell shell("ls", "c:\\workspace"); BAD creates temporary !!
shell.executeCommand();
}
If I send my second argument direclty as a const char* I know that it will create a temporary and my constructor argument will only be visible in the constructor, then its lost, because of the reference to temporary.
My question is, why isnt the same thing happening to the first argument, the string, because I am sending him also as a const char* directly, but the value isnt lost outside of the constructor ?
Shell shell("ls", Path); // BAD - just happens to work as you expected.
Undefined Behavior - Anything can happen, including acting like you expect. The memory allocated by the temporary just happened to not be overwritten in your case.
std::string *ref;
{
std::string tmp("temp");
ref = &ref; // risky, need to keep lifetime of tmp in mind
const char* const rawptr = ref->c_str(); // equivalent to tmp.c_str()
tmp = "altered"; // *ref also modified, rawptr completely invalid
}
// *ref _and_ rawptr completely invalid.
Is there a specific reason why you store references instead of copies?
By changing your class from
class Shell {
private:
const string& command;
const path& firstPath;
const path& secondPath;
...
to
class Shell {
private:
const string command;
const path firstPath;
const path secondPath;
...
you can avoid lot's of your problems and both cases will be correct.
Why this is the case you can see in the answer of sehe