issue with pointers and constructors - c++

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 ?

Related

C++ derived Class constructor

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++;
}
};

how to design class that has char* pointer as class member variable?

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).

Implicitly convert from std::string to eastl::string

I was using string class from stl std::string now I want to replace everything with eastl. So the easy question:
eastl::string obj = std::string("test");
error: conversion from ‘the::string {aka std::basic_string}’ to
non-scalar type ‘eastl::string {aka eastl::basic_string}’
requested
Is it possible to automate conversion between this types?
eastl::string obj1 = "test"; // works out of the box
eastl::string obj2 = std_string.c_str(); // equivalent to above
eastl::string obj3(&std_string[0], // another option
&std_string[0]+std_string.size());
No, not if eastl::string does not define a copy constructor/assignment operator for std::string.
The simplest way to go from a std::string to an eastl::string would be to use the .c_str() method to get a pointer to the std::string's internal char array
std::string ss("hello");
eastl::string es = ss.c_str();
You can add your own if you want to modify the library. Although that is probably a bad idea
simple example:
class MyString {
private:
char *str;
public:
MyString(const std::string &s) {
str = new char[s.length() + 1]; /* allocate */
strcpy(this->str, s.c_str()); /* copy */
}
~MyString() {
delete [] str;
}
};
Then you can create a new MyString with:
MyString ms = std::string("hello");
These classes are not compatible. eastl::string has no constructor with std::string argument. So you should use something like this:
srd::string stlstr = "test";
eastl::string eastlstr= strlstr.c_str();

newbie c++; adding char* arrays?

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.

Create String object from std::string by overloading = operator

I've tried several options but my compiler does not pick up the operator overloading or something else is wrong. I'm using XCode 4.5.2 with default Apple LLVM compiler 4.1.
The error I get is this: Assigning to 'cocos2d::CCString *' from incompatible type 'const char [5]'
on these lines:
CCString *s_piece__locks = "TEST";
cocos2d::CCString *s_piece__locks2 = "TEST";
My .h code:
CCString& operator= (const std::string& str);
// CCString& operator= (const char* str); // this doesn't work either
const CCString& operator = (const char *);
My .cpp code (even though this is irelevant):
CCString& CCString::operator= (const std::string& str)
{
m_sString = CCString::create(str)->m_sString;
return *this;
}
const CCString& CCString :: operator = (const char* str)
{
m_sString = CCString::create(str)->m_sString;
return *this;
}
Your help is very appreciated, thanks!
The error message Assigning to 'cocos2d::CCString *' from incompatible type 'const char [5]' suggests that you are assigning a char array to a pointer to cocos2d::CCString.
This should work:
char bar[] = "ABCD";
cocos2d::CCString foo;
foo = bar;
CCString *s_piece__locks = "TEST";
cocos2d::CCString *s_piece__locks2 = "TEST";
What the heck is this supposed to do? Declaring a pointer does not generate any object except the pointer itself. So basically, for this to "work", there would need to be another CCString object around already, that happens to represent the string "TEST". But even if that's given, how is C++ supposed to know which one to point to? It would need to look "TEST" up in some kind of e.g. hash map.
None of this makes any sense. Change your code to either
Direct use of object on stack:
cocos2d::CCString s_piece;
s_piece = "TEST";
Assigning new content to an object that resides somewhere else. You'd normally use a reference for this, e.g.
void assign_test_to(cocos2d::CCString& target) {
target = "TEST";
}
it's also possible with a pointer
void assign_test_to_ptr(cocos2d::CCString* target) {
*target = "TEST";
}
but don't do that unless you have a specific reason to.
In principle, there's another possibility:
cocos2d::CCString* s_piece_locks = new CCString;
*s_piece_locks = "TEST";
but you want to avoid this, as it can very easily lead to memory leaks. What would be ok is
std::unique_ptr<cocos2d::CCString> s_piece_locks = new CCString;
*s_piece_locks = "TEST";