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++;
}
};
Related
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'm having a little bit of a hard time explaning the problem, so here's a simple rundown of my code:
Imagine I have a class called 'character'
#include "myEnums"
#include "weapon"
character {
protected:
string characterName;
weapon* myWeapon;
public:
string getCharacterName();
void setCharacterName( string );
string getMyWeapon();
void setMyWeapon();
}
Then within 'setMyWeapon' I use this simplified code.
void character::setMyWeapon() {
this->myWeapon = new weapon("longsword");
//this->myWeapon = new weapon(myEnums::LONGSWORD); //Ideally this
}
string getMyWeapon() {
return this->myWeapon.tostring();
}
But when I type the '.' for 'myWeapon' there's no members, anyone know whatup? Assume 'tostring' is defined in 'weapon.h'...
Since myWeapon is a pointer, you need to dereference it to access the pointee's members:
myWeapon->tostring()
// ^^^^
I have a simple class called String which has as a private field a char*.
class String {
char *s;
+ some public methods
};
I want to overload the + operator so a + b would mean that the strings from a and b are concatenated.
The function is here:
String String::operator+(String a)
{
String rez;
rez.s = new char[strlen(this->s) + strlen(a.s) + 1];
assert(rez.s);
strcpy(rez.s, this->s);
strcat(rez.s, a.s);
cout<<rez.s<<endl; // HERE rez.s CONTAINS THE RIGHT STRING!
return rez;
}
After I call this:
c = a + b;
i get an error called Debug assertion failed.
Any ideas?
First, read up on the Rule of Three
Then, consider this:
class String {
char *s; // << pointer
+ some public methods
};
"+ some public methods" better have a constructor that initializes the pointer member to a testable value (like NULL) or you're well-into undefined behavior. It better override the copy-constructor and assignment operators to properly duplicate the string from one String object to another. Finally, it better have a destructor that knows how to clean up a dynamic pointer to the content allocated in all of the above.
I strongly suggest you read that article backwards and forwards.
class Base {
protected:
string m_strName;
char* pchChar;
public:
Base()
{
m_strName = "Base";
pchChar = NULL;
};
void display()
{
printf(" display name : %s %c\n",m_strName.c_str(),pchChar);
};
};
class Derived : protected Base {
public:
Derived()
{
init();
};
void init()
{
m_strName = "Derived";
pchChar = (char*)malloc(sizeof(char));
strcpy(pchChar,"A");
printf(" char %c\n",*pchChar);
display();
};
};
int main()
{
Derived* pDerived = new Derived();
return 0;
}
The observed output is
char A
display name : Derived P
whereas i expected pchChar should have value "A" on both occasions.
am i missing any piece of information??
pls suggest.
You forgot *:
printf(" display name : %s %c\n",m_strName.c_str(), *pchChar);
It should be *pchChar, not pchChar. Because you're printing it as %c.
Or you can use %s as format string, and your printf would work, if the c-string is null-terminated string. Currently its not null-terminated. You should be doing this:
pchChar = (char*)malloc( 2 * sizeof(char)); //2 chars, one for `\0`
strcpy(pchChar,"A");
Or even better use new, and std::cout.
Also, don't forget to call free with malloc to deallocate the memory once you're done with it. And if you use new, use delete.
printf(" display name : %s %c\n",m_strName.c_str(),pchChar);
should be:
printf(" display name : %s %c\n",m_strName.c_str(),*pchChar);
The first will coerce the pointer into a character and will therefore depend entirely upon where that character is located in your address space.
The second will correctly dereference the pointer to get at the character, exactly as you have done so in the init code of your derived class.
And this little snippet:
pchChar = (char*)malloc(sizeof(char));
does not allocate enough space for the "A" string, you need two characters for that:
pchChar = (char*)malloc(2); // sizeof(char) is ALWAYS 1 !!!
Or better yet, don't use malloc at all. Although C++ provides those facilities for C compatibility, you're probably better off using new and delete.
I have a very simple class named person which is given below , I have a problem with only two functions , i.e setstring () function and setname() function , I am calling setstring() function from the setname function.
The only problem is when in the main function I write
Object.setname(“Zia”);
The result is ok as shown in the output screen,
Now when I write
Object.setname(“Zia ur Rahman”);
Nothing is displayed as you can see the output screen.
I know the problem is when I pass the name pointer to setstring () function but I am confused about it please explain it in detail that what is happening here.
#include<iostream.h>
class person
{
char* name;
public:
person();
void setname(const char*);
void setstring(const char*, char*);
void print()const;
};
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0';
}
}
void person::setname(const char* n)
{
setstring(n, name);//passing data member name
}
void person::print()const
{
cout<<"Name: "<<name<<endl;
}
main()
{
person object;
object.setname("Zia ur Rahman");
object.print();
system("pause");
}
alt text http://img264.imageshack.us/img264/8867/inheritanceimage004.jpg
alt text http://img263.imageshack.us/img263/3770/inheritanceimage003.jpg
The specific reason that nothing is being printed is that in setstring, p is copy of the name pointer, not a reference to it. Try changing the signature of setstring to:
void setstring(const char* s, char*& p);
(note the &).
See the other answers for other significant errors in the code - unless these problems are fixed, you are likely to get crashes or strange behaviour.
And unless the purpose of the code is just to learn dynamic arrays, use std::string instead :-).
If you want to do correct OO-oriented programming in C++ you should maybe stay away from direct pointer management but use the STL string class and use references instead of pointers. Then you should have an easier time and your source should produce the correct output.
Otherwise check your constructor of the person class, the name-array has just 3 elements but you're referencing the 4th one there (indices 0-2!). Also in the setstring() method you're not allocating enough space for the trailing '\0' in the array!
For starters:
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
name has three elements - you are treating it as if it had five. In a similar vein:
p=new char[strlen(s)];
should be:
p=new char[strlen(s) + 1];
In fact, that function is completely wrong - you are supposed to be copying the string into 'name', not the mysterious 'p' parameter. If you really want that function, then 'p' must be a pointer to a pointer or a reference to a pointer.
Have you thought about using std::string from STL?
Though first problem I see is this.
person::person()
{
name=new char[3];
strcpy(name,"NILL");
name[3]='\0';
}
Your allocating an array of char's, the size of the array is 3 characters, then your copying "NILL" into it with strcpy so your filling in the array with all the characters but without the null terminator \0. "NILL" is a const string and such has a null terminator that is implicit but not shown for example "NILL\0". The \0 is a control character that is used to indicate the end of the string. Then you have an index out of bounds when you access the 3rd element of name array,when your size of your array is 3.
To help you find out the other parts that could be going wrong, here are some of the links to the function you use.
strcpy
strlen
strcpy will copy the whole string from one buffer to the next including the null terminator control character.
Strlen will return amount of characters between the beginning of the string and the terminating null character. Though it will not count the null character as a character. Thats why you will see suggestions with strlen(string)+1 to include the null terminator.
Though your doing well keep up the work, you will understand these little gotcha when using the standard library functions.
Suppose s have 5 chars. Then new char[strlen(s)]; allocates the memory of 5 chars. Then p[strlen(s)]='\0' is equivalent to p[5]=0. It's very very bad. p[5] does not exsist.
void person::setstring(const char* s, char*p)
{
if(s!=NULL)
{
delete[] p;
p=new char[strlen(s)];
strcpy(p,s);
p[strlen(s)]='\0'; //<<<< LOOK! Out of range
}
}
It's rather C, than C++ code.
There is C++ equivalent:
#include <string>
#include <iostream>
class person
{
std::string name_;
public:
person();
//preferable design is to set object invariant in appropriate constructor
explicit person(const std::string &name);
std::string get_name() const;
void set_name(const std::string &name);
void print()const;
};
person::person()
: name_("NILL")
{}
person::person(const std::string &name)
: name_(name)
{}
std::string person::get_name() const
{
return name_;
}
void person::set_name(const std::string &name)
{
name_ = name;
}
void person::print()const
{
std::cout<<"Name: "<<name_<<std::endl;
}
int main()
{
person person1;
person1.set_name("Zia ur Rahman");
person1.print();
//this is better design decision
//because after constructor we have valid object
person person2("Zia ur Rahman");
person2.print();
std::cin.get();
return 0;
}