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.
Related
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
From reading other stackoverflow questions I am aware that this error means I am trying to dereference a null pointer. However, I cannot figure out where my code is dereferencing a null pointer. I am trying to set a char* (cstring) to a non-null value, but I get an access violation error. Here is the code:
void Data::setName(char const * const name)
{
if (this->name)
delete[] this->name;
this->name = new char[strlen(name) + 1]; // This is where my code breaks in debug mode
strcpy(this->name, name);
}
name is a char* variable that gets initialized to null. setName is being called by an overloaded assignment operator:
Data& Data::operator=(const Data& data2)
{
//if it is a self copy, don't do anything
if (this == &data2)
return *this;
else
{
setName(data2.name); // Here is the call to setName
return *this;
}
}
P.S. For the love of god please don't tell me I shouldn't be using cstrings! I know std::string is better, but this is for a homework assignment that requires cstrings.
If this is the line the code breaks:
this->name = new char[strlen(name) + 1];
then name must be a null pointer, since nothing else is being dereferenced. name is being dereferenced inside the strlen function. Just print the variable value in your debugger and you will be sure.
Also, using same name of variable in the setter like this:
struct A
{
void set(int a){this->a = a;}
int a;
};
is not a good practice. Just use:
struct A
{
void set(int na){a = na;}
int a;
};
or
struct A
{
void set(int a){a_ = a;}
int a_;
};
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.
I am reading some C++ text. In an example, the text written:
class Student {
int no;
char grade[M+1];
public:
Student();
Student(int, const char*);
const Student& set(int, const char*);
void display() const;
};
Student::Student() {
no = 0;
grade[0] = '\0';
}
Student::Student(int n, const char* g) {
*this = Student(); // initialize to empty
set(n, g); // validate, reset if ok
}
I don't understand this line: *this = Student();
Why do we have to do that, while just calling Student(); also makes the default constructor invoked?
Thanks
It's not possible to call the default constructor directly (C++ FAQ). ie.
Student::Student(int n, const char* g){
Student();
set(n, g); // validate, reset if ok
}
doesn't work. However I'm not sure about the solution you have, either.
*this = Student()
will call Student::operator=(const Student&). In this particular class it's OK (that function is the default member copy) but it might not be in general, because the Student object is only 'partly constructed' when that method is called.
Better to have a private init function
void Student::init() {
no = 0;
grade[0] = '\0';
}
and call it from both constructors.
Although, imho, it would be better to use common initialization function, something like this:
class Student {
(...)
private:
void initMe();
};
Student::Student() {
initMe();
}
Student::Student(int n, const char* g) {
initMe(); // initialize to empty
set(n, g); // validate, reset if ok
}
void Student::initMe() {
no = 0;
grade[0] = '\0';
}
That would avoid unnecessary creation of objects.
It constructs a temporary Student object, and then copies it to *this. I would just initialize the member variables to empty in the second constructor though. The idea is that you don't have to write the same code that initializes the member variables as empty twice though, but it is quite trivial problem here.
*this = Student(); is just to initialize the member variables with respect to default constructor called. Such design should be avoided, as it creates temporary and copies it contents.
Use something like below:
void reset() { // introduce this method inlined in the class
grade[no = 0] = '\0';
}
Student::Student() {
reset(); // call it when needed
}
Student::Student(int n, const char* g) {
reset(); // initialize to empty
set(n, g); // validate, reset if ok
}
Let's say I have the following:
char cipan[9];
then what should I pass to the function? how about the get and set method??
I'm currently doing like this
set method
void setCipan(char cipan[]){
this->cipan = cipan;
}
and the get method
char getCipan(){
return cipan;
}
and I get an error when compiling??
Im totally blur.. can someone explain what should i pass to the function??
class userData{
private:
string name;
char dateCreate[9];
void userDataInput(string name,char dateCreate[]){
this->name = name;
this->dateCreate = dateCreate;
}
public:
//constructor
userData(string name){
userDataInput(name,dateCreate);
}
userData(string name,char dateCreate[]){
userDataInput(name,dateCreate);
}
//mutator methods
void changeName(string name){this->name = name;}
void changeDateCreate(char *dateCreate){this->dateCreate = dateCreate;}
//accesor methods
string getName(){return name;}
char *getDateCreate(){return dateCreate;}
};
I'd do the following:
void setCipan(const char* new_cipan)
{
strcpy(cipan, new_cipan);
}
const char* getCipan() const
{
return cipan;
}
Of course, the better approach is to use std::string:
void setCipan(const string& new_cipan)
{
cipan = new_cipan;
}
string getCipan() const
{
return cipan;
}
Constructor's purpose is to initialize class variables. I think it's unnecessary to call another method in the constructor to do initialization.
void userDataInput(string name,char dateCreate[]){
this->name = name;
this->dateCreate = dateCreate; // Both the dateCreate are class variables.
}
userData(string name){
userDataInput(name,dateCreate); // dateCreate is already a class variable.
}
dateCreate is the class scope variable. You are just passing it to a method, and re-assigning the same to dateCreate. Assignment operation doesn't copy elements of one array to another and are invalid operations.
To copy array elements, use std::copy instead.