So there seems to be some problem with the solution to Problem 9-2 in the book "Object-Oriented Programming in C++, 4th edition" by Robert Lafore. So the problem is that if I would like to create a Pstring object with a statement like Pstring = "This is a string", the Pstring constructor will only call the constructor with no arguments in the String class, instead of the second one with uses one char[] argument. Does anyone know what causes this kind of problem, and a fix to this? Thanks!
#include <iostream>
#include <cstring>
using namespace std;
////////////////////////////////////////////////////////////////
class String //base class
{
protected: //Note: can't be private
enum {
SZ = 80
}; //size of all String objects
char str[SZ]; //holds a C-string
public:
String() //constructor 0, no args
{
str[0] = '\0';
}
String(char s[]) //constructor 1, one arg
{
strcpy(str, s);
} // convert string to String
void display() const //display the String
{
cout << str;
}
operator char*() //conversion function
{
return str;
} //convert String to C-string
};
////////////////////////////////////////////////////////////////
class Pstring: public String //derived class
{
public:
Pstring(char s[]); //constructor
};
//--------------------------------------------------------------
Pstring::Pstring(char s[]) //constructor for Pstring
{
if (strlen(s) > SZ - 1) //if too long,
{
for (int j = 0; j < SZ - 1; j++) { //copy the first SZ-1
str[j] = s[j]; //characters "by hand"
str[j] = '\0';
} //add the null character
} else
//not too long,
String(s); //so construct normally
}
////////////////////////////////////////////////////////////////
int main() { //define String
String s1 = "This is a string"; // This works great
s1.display();
Pstring s2 = "This is a string"; // *** Here, nothing will be assigned to s2****
s2.display(); // *** Nothing will be printed here***
return 0;
}
In a function parameter, a T[] (where T is char in your case) is really a T*.
In C++, a string literal is a const char[N] fixed array, which decays into a const char* pointer to the 1st element. But you don't have any constructors that accept either of those types as a parameter. A const char* can't be given to a char*. You need to add const to your constructors:
String(const char s[])
Pstring(const char s[])
Also, calling String(s) in the body of the Pstring constructor does not initialize the Pstring object using the base class String constructor, like you are expecting. It instead constructs a temporary String object that goes out of scope immediately. The Pstring object is not affected by that.
The only place that a base class constructor can be called by a derived constructor is in the member initialization list. In your case, there is no such call, so the compiler implicitly calls the base class default (0-param) constructor before entering the body of the derived constructor. Which doesn't help you, since you want the base class to initialize the str buffer with data.
One way you can do that is add another constructor to String that takes a user-defined length as input, and then call that from the Pstring constructor, eg:
String(const char s[], size_t len)
{
len = std::min(len, SZ-1);
memcpy(str, s, len);
str[len] = '\0';
}
Pstring::Pstring(const char s[])
: String(s, strlen(s))
{
}
Note that your 1-param String constructor has a buffer overflow waiting to happen, since the user can directly construct a String object with input that is greater than SZ characters in length. The String constructor should use strncpy() instead of strcpy():
String(const char s[])
{
strncpy(str, s, SZ);
str[SZ-1] = '\0'; // in case s is >= SZ chars
}
Which then makes the 1-param Pstring constructor redundant - especially since it is not handling the null terminator correctly to begin with, as the assignment of the terminator needs to be outside of the for loop, eg:
Pstring::Pstring(const char s[])
{
if (strlen(s) >= SZ)
{
for (int j = 0; j < SZ - 1; j++) {
str[j] = s[j];
}
// alternatively: memcpy(str, sz, SZ-1);
str[SZ-1] = '\0'; // <-- moved here
}
else
strcpy(str, s);
}
In this conversion constructor
Pstring::Pstring(char s[]) //constructor for Pstring
{
if (strlen(s) > SZ - 1) //if too long,
{
for (int j = 0; j < SZ - 1; j++) { //copy the first SZ-1
str[j] = s[j]; //characters "by hand"
str[j] = '\0';
} //add the null character
} else
//not too long,
String(s); //so construct normally
}
at first the default constructor of the class String is called before the control will be passed to the constructor of the class Pstring.
So the data member is set like
String() //constructor 0, no args
{
str[0] = '\0';
}
As the argument that is the string literal "This is a string" that by the way as the argument has the type const char * due to the implicit conversion of arrays to pointers has the length that is less than SZ then within the body of the constructor Pstring nothing is done with the data member str. This statement
String(s);
creates a temporary object of the type String that is at once deleted.
What you need is to write at least
strcpy( str, s );
instead of creating the temporary object.
Pay attention to that the constructors with parameters shall be declared like
String( const char s[] );
and
Pstring( const char s[]);
if you are going to use string literals as arguments of the constructors.
You could move this code snippet
if (strlen(s) > SZ - 1) //if too long,
{
for (int j = 0; j < SZ - 1; j++) { //copy the first SZ-1
str[j] = s[j]; //characters "by hand"
str[j] = '\0';
} //add the null character
} else
//not too long,
String(s); //so construct normally
form the constructor Pstring to the constructor String with parameter and substitute it for one call of strncpy like
strncpy( str, s, SZ - 1 );
str[SZ-1] = '\0';
In C++, constructors aren't allowed to be called like this:
else
//not too long,
String(s);
C++ wants you to use its initialization list instead (see the link above for some examples).
If you have a portion of the construction in the parent class you would like to call from inside the child constructor, you can use a protected method instead:
class String //base class
{
protected:
void commonTask(char s[]) {
// do something...
}
public:
String(char s[])
{
commonTask(s);
}
};
class Pstring: public String
{
public:
Pstring(char s[]) { //constructor
if(someCondition) {
commonTask(s);
}
}
};
I'm using pseudo code here, but hopefully you get the idea.
I am trying to write my own string class using the library for preparing my exam .But I had this error saying that
main.cpp:9:22: error: no match for ‘operator+’ (operand types are ‘const
char [5]’ and ‘MyString’)
MyString c = "Hola" + b;
My main is like this. It works file then "Hola" and b change place.
MyString b("Mundo\n");
MyString c = "Hola" + b;
I think it doesn't call the constructor there .
My class has these in private.
char * _str
int _length
My constructor.
MyString::MyString(const char * str){
int length = 0;
for(char c = str[0]; c != '\0' ; c++)
++length;
_length = length;
_str = new char[length+1];
strcpy(_str,str);
}
And my + overload
const MyString MyString::operator+(const MyString& mS) const{
char * tempChar = new char[_length + mS._length];
MyString tempStr(tempChar);
delete[] tempChar;
strcpy(tempStr._str,_str);
strcat(tempStr._str,mS._str);
return tempStr;
}
Edit : I solved it by making operator+ a friend function but I want to know why
when you write
MyString b{"Mundo\n"};
MyString c = b + "Hola";
string literal will be sent your operator+ function paremeter, and MyString object created with "Holla"(You can test it with write a text in your constructor).
Besides, when you didn't write your friend function and write
MyString b{"Mundo\n"};
MyString c = "Holla" + b;
compiler will search function which has first parameter is const char * and can't find.(and no match error show this). this is the reason for writing friend function.
I hope you can understand reason
I am trying to initialize a 2D matrix inside my constructor which i will furthur change according to my requirement.
class Player{
string pName;
char playerBoard[ROW][COL];
public:
Player(string name){
this->pName=name;
for(int i=0;i<ROW;i++){
for(int j=0;j<COL;j++){
this->playerBoard[i][j] = ".";
}
}
}
But I'm getting the following error
[Error] invalid conversion from 'const char*' to 'char' [-fpermissive]
Any alternate way to initialize this?
You are trying to assign a string literal(const char*) to a char array element. Change the double quotes to single quotes to indicate that this is a char and not a string literal(null terminated C string represented by a const char*).
class Player{
string pName;
char playerBoard[ROW][COL];
public:
Player(string name){
this->pName=name;
for(int i=0;i<ROW;i++){
for(int j=0;j<COL;j++){
this->playerBoard[i][j] = '.'; //<-chage this
}
}
}
I have a Test class with overloaded constructor. Initializing const char array member by string literals work fine. But, initialization by const char * gives error -
error: incompatible types in assignment of ‘const char*’ to ‘const
char [25]’
class Test
{
const char d_arr[25];
public:
Test() : d_arr("Test Class") {}
Test(const char * arr) : d_arr(arr) {}
};
How to resolve this?
You are assigning a pointer to an array, which is not allowed.
i.e. You cannot do following:
const char *arr = "ABC";
const char d_arr[25] = arr;
What you need to do is copy the chars manually i.e. something like:
Test(const char * arr) {
size_t index = 0;
if (arr) {
while (arr[index] && index < 24) {
d_arr[index] = arr[index];
++index;
}
}
d_arr[index] = 0;
}
That all said, as said in comments, its better to use std::string.
sorry, I'm not a very advanced programmer, could you guys help me find a solution for this?
class Person
{
private:
char name[50];
double age;
public:
void setName(char []);
void setAge(int);
char* getName();
double getAge();
};
void Person::setAge(int a)
{
Person::age = a;
}
char* Person::getName()
{
return name;
}
double Person::getAge()
{
return age;
}
void Person::setName(char n[])
{
Person::name = n;
}
and it keeps giving me
"[Error] incompatible types in assignment of 'char*' to 'char [50]'"
I want to make this work without using an overloaded assignment operator, please help
You can use strncpy (requires n to be '\0' terminated):
void Person::setName(char n[])
{
strncpy(name, n, sizeof(name)/sizeof(name[0]) - 1);
}
or make name an std::string then your original code will work. Also instead of writing char n[] its better to const char* n as n in your setName function is actually a pointer to first element. Also by making it const you allow to pass string literals to your function.
this is what I did, thanks guys for answering, I appreciate it :D
char *name;
void Person::setName(char n)
{
name = new char[n];
}
Just replace char name[50]; with char *name and it will work just fine..