Currently in the process of learning c++ and maybe it's because I am really frustrated right now but I really can't seem to wrap my simple little head around this:
There's a class constructor:
Class (const char* file);
I use it like this in my main class:
char* chararr = new char[2048];
//stuff with charrarr
// std::cout << chararr would be: "C:\stuff\paths\etc\"
Class c( strcat(chararr , "filename.file"));
//I want it to be: "C:\stuff\paths\etc\filename.file
Class c2( strcat(chararr , "filename2.file2"));
////I want this to be: "C:\stuff\paths\etc\filename2.file2" but it is instead
// "C:\stuff\paths\etc\filename.filefilename2.file"
The problem is that strcat modifies the chararr so the second time I do this, with Class c2, it gets all messed up... I'm guessing it's a very very basic thing to do and it gets me even more frustrated knowing I'm missing something really obvious...
Error in you code first time you should call strcpy(), whereas you are concatenates with garbage.
Class c( strcpy(chararr , "filename.file"));
else it concatenate with garbage, undefined behavior.
Edit:
// std::cout << chararr would be: "C:\stuff\paths\etc\"
size_t len = strlen(chararr); // <--- notice
Class c( strcat(chararr , "filename.file"));
// path\path2\filename.file
// ^ replace `f` with '\0'
chararr[len] = '\0'; // <--- notice
Class c2( strcat(chararr , "filename2.file2"));
Why are you using strcat when you don't need (as it seems) to concatenate strings? You could simply do:
class Class {
public:
Class(const char* file) {}
// ...
};
int main() {
Class c("filename.file");
Class c2("filename2.file2");
}
or, if you really need to concatenate strings use std::string instead of const char*.
std::string is easier to manipulate, grows in size automatically and deletes itself automatically. The only gotcha in this case, is that the class must make its own copy of the text you pass as the pointer, because when the string changes data or goes out of scope, the pointer is no longer valid.
std::string mystr = "filename.file";
MyClass c(mystr.c_str());
mystr = "filename2"; // reset string
mystr += ".file2" // concatenate
MyClass c2(mystr.c_str());
If you wrote the class, change it to use std::string as well.
class MyClass
{
public:
MyClass(const std::string& str_in)
: str(str_in) // initialization list
{
}
std::string str;
};
std::string mystr = "filename.file";
MyClass c(mystr);
mystr = "filename2"; // reset string
mystr += ".file2" // concatenate
MyClass c2(mystr);
You have to reinitialize chararr if you want to use it this way:
char* chararr = new char[2048];
strcpy(chararr,"path\\path2\\");
Class c( strcat(chararr , "filename.file"));
strcpy(chararr,"path\\path2\\");
Class c2( strcat(chararr , "filename2.file2"));
For that you're using c++ I'd recommend using std::string anyway.
Related
I'm learning C++ inheritance and i have some problems with it.
I'm using Zinjal IDE with GNU c++ compiler.
This is the code:
class String {
protected:
char str[MAX_STR_SIZE];
public:
String(char str_init[])
{
strcpy(str, str_init);
}
};
class PString : String
{
public:
PString(char str_init[])
{
if (strlen(str_init) > MAX_STR_SIZE)
strncpy(str, str_init, MAX_STR_SIZE-1);
else
String(str_init);
}
};
Well it's just creating my own "string" class. But: What is the problem? The string can go too much bigger. So when the constructor of my "String" Class is called with a string the superssed "MAX_STR_SIZE" (which is defined as 80 chars for expamle) the program will crash with an array overflow ( >80 chars).
So i want to create a "child" or "derived" class called "PString" which can handles overflow.
As you can see PString child Class constructor checks the string if it's > MAX_STR_SIZE. If it's bigger than what char array can handle it cut off the string by MAX_STR_SIZE, so avoiding the overflow. If its smaller than MAX_STR_SIZE it calls the Parent class constructor.
But, g++ fails telling me "No matching function call to 'String::String()'.
It's a lame error i know, but i'm just learning
Thanks in advance.
You can't just call a constructor out of the blue. There are only specific places you can call a constructor and this isn't one of them.
The compiler sees String(str_init); and ssumes it is a function call but you don't have a matching function - hence the error.
As per the comment below there is a subtlety here. The error message is "No matching function call to 'String::String()". In a class called String a method called String is going to be a constructor. Therefore the line String(str_init); isn't a function call, it is trying to make a String item using a non-existent default constructor.
what you are trying to do does not require this set of complications. In general strncpy will handle both cases correctly. However, for the education sake here is some analysis
1.In your constructor of the PString you call the construct of the String and ask it to strcpy str_intit into str. So, you cause the same memory corruption you wanted to avoid. you are missing String() constructor. I suggest to create the default constructor for the String and remove the one you use from PString.
String() {str[0] = 0;}
..
PString(char str_init[])
{
2.your case could be modified a bit as the following:
if (strlen(str_init) > MAX_STR_SIZE) {
strncpy(str, str_init, MAX_STR_SIZE-1);
str[MAXLEN-1] = 0; // strncpy will not add '0' in this case. you need to do it yourself
}
else
strcpy(str, str_init);
above, in the if clause you use strncpy whether after the 'else' you can use the simple strcpy. So, it looks like this:
#include <cstring>
#define MAX_STR_SIZE 80
class String {
protected:
char str[MAX_STR_SIZE];
public:
String(char *str_init)
{
strcpy(str, str_init);
}
String() {
str[0] = 0;
}
};
class PString : String
{
public:
PString(char str_init[])
{
if (strlen(str_init) > MAX_STR_SIZE) {
strncpy(str, str_init, MAX_STR_SIZE-1);
str[MAX_STR_SIZE-1] = 0;
}
else
strcpy(str, str_init);
}
};
2 Errors.
First, you need to invoke the base constructor via your class' initializer list as explained in this answer.
Second you need to assign some object to your class when calling
String(str_init);
The following code compiles on my machine.
#include <cstring>
#define MAX_STR_SIZE 20
class String {
protected:
char str[MAX_STR_SIZE];
public:
String(char str_init[])
{
std::strcpy(str, str_init);
}
};
class PString : String
{
public:
PString(char str_init[]) : String(str_init)
{
if (strlen(str_init) > MAX_STR_SIZE)
strncpy(str, str_init, MAX_STR_SIZE-1);
else
String s = String(str_init);
String s2(str_init);
int i = 0;
i++;
}
};
First i want to introduce my situation :
I have write some classes that has char* pointer as private class member.And also this project has GUI, so when click buttons,some functions may execute more than one time.Those classes are designed single class in project.But some functions of them can execute more than one time.Then I found my project has memory leak.
so i want to ask the following questions:
how to design the set function?
how to design the other functions that use the char* member variable?
how to design the class operator= function?
for example:
class A:
{
public :
setStr(char * s){//need new or just use =?};
A & operator=(const A& other){//also need new?};
manyTimesFunctions(char * other)
{
//need to use chars other to assignment str
//how to carefully use new to avoid memory leak?
//other may be another class's locality none const variable
}
private:
char * str;
}
So ,the project only init class A once,but may use setStr and manyTimesFunctions many times.
May be the answer:
I think i have found what i need to take care of:copy that class,that answers are really useful to me.
Just use std::string. It takes care of memory management for you. The member declaration then looks like
std::string str;
and the setter function looks like
void setStr( char const* s ) { str = s; }
Where you want to use the string and need a char const*, just write str.c_str().
With use of standard library types like std::string, and no manual dynamic allocation, you generally don't need to be concerned about operator=: the compiler-generated copy assignment works nicely.
By the way, it's generally a good idea to decide on some naming convention for member variables. Common ones for C++ include str_, mStr, and my_str. The underscore suffix is perhaps the most common one, but don't use a leading underscore like _str, because although technically allowed it conflicts with the conventions for implementation defined names (e.g. leading underscore is not allowed for identifiers in the global namespace).
I am not 100% sure what you are trying to do. However, since char* is a pointer you may be able to simply pass around the references.
char* operator=(char* s) { str = s; }
Just know that then if you modify value in your function it will modify the place you copied it from
If the char* needs to actually be a clone, so that it does not modify the original value. You first need to obtain the length of the char*.
This can be done with this function
unsigned Length(char* s)
{
unsigned I = 0;
while( *(s+I) != '\0')
I++;
return I;
}
The a new string can be created as follows
str = new char[LENGTH];
At that point you can copy the string over term by term
for(I = 0 ; I < LENGTH; I++)
{
str[I] = s[I];
}
Finally to avoid memory leaks this needs to be deleted in the class destructor
~A()
{
delete [] str;
}
Of course using std::string could save a lot of problems.
This answer will be used to contrast what the other answer(s) given that state to use std::string (and those answers are correct -- use std::string).
Let's assume that you could only use char *, you can't for some reason use std::string, and that you are dealing with NULL terminated strings. This is a synopsis of what your implementation would have to do (and please compare this with simply using std::string):
#include <algorithm>
#include <cstring>
class A
{
public:
// construct empty string
A () : str(new char[1]()) {}
// construct from non-empty
A(const char *s) : str(new char[strlen(s) + 1])
{ strcpy(str, s); }
// copy construct
A(const A& rhs) : str(new char[strlen(rhs.str) + 1])
{ strcpy(str, rhs.str); }
// destruct
~A() { delete [] str; }
// assign
A& operator=(const A& rhs)
{
A temp(rhs);
std::swap(str, temp.str);
return *this;
}
// setter
void setStr(char * s)
{
A temp(s);
*this = temp;
}
// getter
const char* getStr() { return str; }
private:
char * str;
};
Live Example
After adding a couple more constructors and a getter function, this follows the Rule of 3.
You see how much code we needed to add just to make the class safely copyable and assignable? That's why using std::string is much more convenient than using char * when it comes to class members. For std::string a single line needs to be changed, compared to adding the copy / assignment (and move, which I didn't show) functions.
The bottom line is that in C++ if you want strings, use strings (std::string) and try to keep away from using char * (unless you have a very compelling reason to be using char * to represent string data).
I was trying to understand Copy constructor and Operator. I read some code but I just didn't get it.
Here is main function
int main()
{
Cwin win1('A',"window")
Cwin win2;
win1 = win2;
win1.set_data('B',"hello");
win1.show();
win2.show();
}
I extract the most important code from Class Cwin
Class Cwin
{
private:
char id, *title ;
public:
Cwin(char i, char *text){......} //constructor
Cwin(){......} //default constructor, id ='D' , *title = "default"
Cwin(const Cwin &win)
{
id = win.id;
strcpy(title,win.title);
}
......
};
Output :
B : hello
B : hello
I can understand what causes it. But I can't understand below solution that fix it.
Class Cwin
{
private:
char id, *title ;
public:
Cwin(char i, char *text){......} //constructor
Cwin(){......} //default constructor, id ='D' , *title = "default"
Cwin(const Cwin &win)
{
id = win.id;
strcpy(title,win.title);
}
void operator=(const Cwin &win)
{
id = win.id;
strcpy (this->title , win.title);
}
......
};
Output:
B : hello
D : default
Why change it strcpy (title , win.title); into strcpy (this->title , win.title); makes a huge difference?
Explanation:
Inside of a member function, you can write this->title or title. Both are equivalent, unless you have a local function variable called title which name would then hide the member.
WHat makes the difference is that you have added an assignement operator.
Without assignement operator your the statement win1=win2 proceeds by copying member by member. In this situation, a pointer is copied as is, so that after the copy, both objects will point to the same pointed char*:
When you then set_data(), the string is copied to the char* pointer by win1, but win2 points to the same value.
Your second code snippet, handles the copy much better: the content of the string pointed to is copied/duplicated to char* pointed by the other object. win1 and win2 will hence continue to use 2 distinct strings.
Remarks/advices
You use strcpy() without checking that the target is long enough to contain the copied string. This might result in buffer overflow, memory corruption, and a lot of other horrible things.
I'd strongly advise that you'll use std::string instead of char* whenever possible: The default copy would have been well managed, and you don't have to care for memory allocation/deallocation,length.
C++ classes have a default assignment operator, that does a shallow copy whenever you assign one member of that class to other member unless you overload it. (except at the time of initialization, where it uses the copy constructor )
Now, coming to your code in the first part there is no overloading of the assignment operator, therefore what it does is do the shallow copy of the elements of win2 to win1 which in turn copies the 'id' and the 'title' pointer(not the string what it is pointing to(storing)).
The main function will run as follows :
int main()
{
Cwin win1('A',"window")
/*
This will create a new object win1
win1.id = 'A' and win1.title will point to "window"
*/
Cwin win2;
/*
This will create a new object win2
win2.id = 'D' and win2.title will point to "default"
*/
win1 = win2;
/*
This will do somewhat the following
win1.id = win2.id;
win1.title = win2.title; ( note that the pointer is copied )
Now , win1.id = 'D' win2.id = 'D'
win1.title points "Defalult"
win2.title points "Default"
*/
win1.set_data('B',"hello");
/*
This sets the win.id = 'B'
and now the win1.title points to "hello"
Also since win1.title and win2.title are both the same pointer
so win2.title will also point to "hello"
*/
win1.show();
win2.show();
}
But in the second part you have overloaded the assignment operator which instead of copying the pointer copies the string it is pointing to(storing).The main will now run as follows :
int main()
{
Cwin win1('A',"window")
/*
This will create a new object win1
win1.id = 'A' and win1.title will point to "window"
*/
Cwin win2;
/*
This will create a new object win2
win2.id = 'D' and win2.title will point to "default"
*/
win1 = win2;
/*
This will now do what is in the overloaded assignment operator
where to copy the string strcpy is used which will copy the content
of the string instead of copying the pointers
Now , win1.id = 'D' win2.id = 'D'
win1.title points "Defalult"
win2.title points "Default"
*/
win1.set_data('B',"hello");
/*
This sets the win.id = 'B'
and now the win1.title points to "hello"
win2.title will still point to "Default"
*/
win1.show();
win2.show();
}
Hence , the given results.
And you must also see and follow the advice given in this answer.
If Cwin does not explicitly implement its own assignment-operator (which your first example does not), the compiler will generate a default implementation that simply copies member values as-is from one object to another. That is why your output said the same thing for both objects - the win1 = win2 statement using the default assigment-operator implementation merely overwrote the pointer value of win1.title with the pointer value of win2.title, and thus both title members were left pointing at the same memory block, which the subsequent win1.set_data() statement populated with data.
To do what you are asking, you should change title to use std::string instead of char*, let it handle the copying for you. It will work fine with the compiler's default copy-constructor and assignment-operator implementations (unless you have other data that you need to make manual copies of):
#include <string>
Class Cwin
{
private:
char id;
std::string title;
public:
Cwin()
: id('D'), title("default") {}
Cwin(char i, const std::string &text)
: id(i), title(text) {}
void set_data(char i, const std::string &text)
{
id = i;
title = text;
}
void show()
{
std::cout << id << " : " << title << std::endl;
}
};
But, if you need to use char* directly, you must implement the copy-constructor and assignment-operator properly to ensure your title data gets copied correctly, eg:
#include <cstring>
Class Cwin
{
private:
char id, *title ;
public:
Cwin()
: id(0), title(NULL)
{
set_data('D', "default");
}
Cwin(char i, char *text)
: id(0), title(NULL)
{
set_data(i, text);
}
Cwin(const Cwin &win)
: id(0), title(NULL)
{
set_data(win.id, win.title);
}
~Cwin()
{
delete[] title;
}
Cwin& operator=(const Cwin &win)
{
if (this != &win)
set_data(win.id, win.title);
return *this;
}
void set_data(char i, char *text)
{
int len = std::strlen(text);
char *newtitle = new char[len+1];
std::strcpy(newtitle, text);
delete[] title;
title = newtitle;
id = i;
}
void show()
{
std::cout << id << " : " << title << std::endl;
}
};
Any time you have to manually allocate memory in a constructor and free it in a destructor, chances are good that you will also need to copy that data in a copy-constructor and assignment-operator as well. Read up about the The rule of three/five/zero. In this situation, CWin needed to follow the Rule of three in order to function properly with char*, but can follow the Rule of zero when using std::string. You should always strive to write code that follows the Rule of zero whenever possible, it makes things easier to manage.
The following code doesn't work:
class String{
public:
char* str;
int* counter;
String(){
str = NULL;
counter = new int;
*counter = 1;
};
String(const char* str1){
String();
str = new char[strlen(str1)+1];
strcpy(str, str1);
};
};
I've changed the call to the empty constructor and replaced it with its internals, and now the following code works:
class String{
public:
char* str;
int* counter;
String(){
str = NULL;
counter = new int;
*counter = 1;
};
String(const char* str1){
//String();
str = new char[strlen(str1)+1];
strcpy(str, str1);
counter = new int;
*counter = 1;
};
Can you please suggest why?
Thanks, Li.
"Doesn't work" is not a good description of the problem. But you apparently tried to invoke a constructor from another one. That's called constructor delegation and is not (yet) supported by C++.
BTW, a class like this should get a user-defined copy constructor, assignment operator and destructor.
It seems to me you are trying to invoke a constructor from another constructor of the same class, like you would in C# for example. Unfortunately you cannot do this in C++ (at least no easy way I know of). You need to either have a private method or duplicate the code.
In the current C++, constructors cannot call each other. That's called "chaining constructors", or "delegating constructors" and is supported by the new C++0x standard but with a different syntax than the one you are using.
Off-topic, why do you use a pointer to int for the counter?
Calling String(); actually creates a temporary object and then throws it away again. It won't call the other constructor.
The call 'String();' creates an unnamed temporary object of type String that is destroyed immediately. The second code snippet is fine.
However you should really relook at the members of your class. Use std::string instead of raw char pointer. Also int *counter does not look very intuitive
You are allowed to do something like this:
class String{
public:
char* str;
int* counter;
private:
void initialize() {
str = NULL;
counter = new int;
*counter = 1;
}
public:
String(){
initialize();
};
String(const char* str1){
initialize();
str = new char[strlen(str1)+1];
strcpy(str, str1);
};
};
why is the counter a pointer ?
What does not work ? compilation error ?
Probably calling the constructor inside another is not well supported on your compiler ?
what platform ?
Just when I thought I had it figured out, I get an exception handling error. Problem: The problem is that the private members lose the information outside of the constructor. Here's my class definition
Code:
class ClassType
{
private:
char *cPointer;
int length;
public:
ClassType();
// default constr. needed when allocating in main.
ClassType( const ClassType* );
char otherFunc();
};
classtype.cpp:
"#include ClassType.h"
ClassType( const ClassType* )
{
cPointer = ClassType->cPointer;
length = ClassType->length;
}
ClassType::ClassType( const char *myVar )
{
cPointer = new char[ strlen( myVar ) + 1 ] //+1 for trailing '\0'
strcpy( cPointer, myVar );
length = strlen( cPointer );
}
char ClassType::otherFunc()
{
cPointer; // Nothing is shown when debugging..
cPointer = "MyPointer"; // Results in acrash
length = 5; // Results in a crash
}
// The main function is working properly.
This isn't valid C++ code.
If you are using C++, shouldn't you
use std::string for the string?
Constructor based on another
instance should be ClassType(const
ClassType& rhs)
I can't think of why it would crash where you indicate, but there are several problems with your code (some of which are compile-time problems, so we can't be sure this code accurately reflects the problem):
there are ownership problems - when ClassType::ClassType( const ClassType* ) is called, which instance of ClassType owns the object pointed to by cPointer?
there's no dtor to release the memory allocated in `ClassType::ClassType( const char *myVar )'
since cPointer may point to something allocated by new or might not, you'll have issues trying to determine when the thing allocated by new should be deleted.
As far as the compile time errors go:
the definition of ClassType( const ClassType* ) should start with ClassType::ClassType( const ClassType* )
the contents of ClassType::ClassType( const ClassType* ) should be using a parameter instead of the ClassType class name as the pointer
char ClassType::otherFunc() needs a return statement
Is this the real code?
ClassType( const ClassType* )
{
cPointer = ClassType->cPointer;
length = ClassType->length;
}
If so, it needs to be like this:
ClassType( const ClassType* rhs )
{
cPointer = rhs->cPointer;
length = rhs->length;
}
Also, this constructor is not the default ctor:
ClassType( const ClassType* ); // default constr. needed when allocating in main.
A default ctor is specifically a ctor that either takes zero parameters or all the parameters have defaults specified. In other words, a default ctor is a ctor that can be called like this:
ClassType myObject;
I provided a pretty complete answer in your other question about this code. I believe that the main issue is that your copy constructor is massively broken. It will cause double free errors and other badness. Also since your destructor calls delete on the pointers you allocate, you can't ever assign string literals to your classes pointers.
Default constructors are those for which all the arguments have default values, so your constructor that takes a pointer is not a default constructor.
Your crash locations indicate the class has not been constructed properly, so you're probably getting address errors when you assign to them.
Could you post main, as that is probably key to see the problem?