This question already has answers here:
What is The Rule of Three?
(8 answers)
Closed 7 years ago.
I am very curious why is copy constructor so important for the dynamic allocation of my own defined class.
I am implementing the low-level c-string class with dynamic allocations and here is a quick view of my class
class String
{
private:
char * buf;
bool inBounds( int i )
{
return i >= 0 && i < strlen(buf);
}
static int strlen(const char *src)
{
int count = 0;
while (*(src+count))
++count;
return count;
}
static char *strcpy(char *dest, const char *src)
{
char *p = dest;
while( (*p++ = *src++));
return dest;
}
static char* strdup(const char *src)
{
char * res = new_char_array(strlen(src)+1);
strcpy(res,src);
return res;
}
static char * new_char_array(int n_bytes)
{
return new char[n_bytes];
}
static void delete_char_array( char* p)
{
delete[] p;
}
public:
/// Both constructors should construct
/// this String from the parameter s
String( const char * s = "")
{
buf = strdup(s);
}
String( String & s)
{
buf = strdup(s.buf);
}
void reverse()
{
}
void print( ostream & out )
{
out << buf;
}
~String()
{
delete_char_array(buf);
}
};
ostream & operator << ( ostream & out, String str )
{
str.print(out);
return out;
}
I know the part of strdup() function is not really correct but I am just doing some tests.
My problem is if I do not have the copy constructor and my main() is
int main()
{
String b("abc");
String a(b);
cout << b << endl;
return 0;
}
The compiler will tell me double free or corruption (fasttop) and I find some answers about this question and see the Big three rules.
Can you guys tell me why my code works without any errors if I have the copy constructor and what the error of double free or corruption (fasttop) means?
If you don't define a copy constructor, the compiler will insert one for you. This default copy constructor will simply copy all the data members, so both instances of String will point to the same area of memory. The buf variable will hold the same value in each instance.
Therefore when the instances go out of scope and are destroyed, they will both attempt to release the same area of memory, and cause an error.
Related
Dear StackOverFlowers,
I'm having trouble passing a const char* [] to an object. The scenario is as follows.
I have a class UlamScreen which contains a const char* [] with several strings. UlamScreen also contains an object homeScreenMenu.
class UlamScreen {
const char* homeScreenText[5] = {"EVA dun", "Sabine", "TPU dun", "test Wout",
UlamScreenMenu homeScreenMenu;
};
class UlamScreenMenu {
private:
const char* _menuText[];
public:
UlamScreenMenu(const char*[]);
void drawMenu();
};
I want to pass the const char* [] to UlamScreenMenu so I can use it in a member function called void drawMenu, like this:
void UlamScreenMenu::drawMenu() {
for (int i = 0; i < menuItems; i++) {
tft.println(_menuText[i]);
}
}
I passed it to UlamScreenMenu's constructor like this:
UlamScreen::UlamScreen() : homeScreenMenu(homeScreenText) {
}
UlamScreenMenu::UlamScreenMenu(const char* menuText[], int length) {
for(int i = 0; i < length; i++) {
_menuText[i] = menuText[i];
}
}
I thought this would work, but for some reason, it does not. tft.println(_menuText[i]); used with void drawMenu does not send anything to my tft screen. When I use tft.println(_menuText[i]); from within the UlamScreen class it works perfectly.
Just to be clear, I can use the tft object within the UlamScreenMenu class because other functions like tft.drawRect() are working correctly.
What is wrong with this way of passing the const char* []? Thanks in advance.
In C++, you can't declare a member variable of type const char* x[], since this would denote a flexible array member. Flexible array members are a C-feature allowing the last member of a struct to be an array of varying size (cf., for example, Arrays of unknown size / flexible array members). Having parameters of type const char* x[] in functions, however, is supported and has basically the same meaning as const char** x.
If you stick to a member of type const char**, then you'll have to handle memory management in that class. This means: take care of allocating, deallocating, copying, moving, copy-assigning, and move-assigning objets of that class (cf, for example, the rule of 0/3/5).
If - as suggested in the comments - you use standard library collections, e.g. std::vector, these classes will do all this stuff in a reliable manner for you. See the following example illustrating the difference between both:
Note that the C++-version probably would not even take a const char*[]-parameter but directly a const std::vector<const char*> &x-parameter. But I kept the const char*[]-parameter in the constructor to provide the same interface in both variants:
// Variant 1: "old" C-style:
class Menu {
public:
Menu(const char* x[], int length) {
m_x = new const char*[length];
m_length = length;
for (int i=0; i<length; i++) {
m_x[i] = x[i];
}
}
~Menu() {
delete[] m_x;
}
// TODO: implement copy- and move constructors + copy- and move assignments
// ...
void print() {
for (int i=0; i<m_length; i++) {
std::cout << m_x[i] << std::endl;
}
}
private:
const char** m_x = nullptr;
int m_length;
};
#include <vector>
// Variant 2: a C++- way:
class Menu2 {
public:
Menu2(const char* x[], int length) {
m_x.assign(x, x+length);
}
void print() {
for (auto s : m_x) {
std::cout << s << std::endl;
}
}
// Menu2 does not manage memory on its own, hence:
// No special copy/move - constructors/assignments to be implemented.
// No special destructor necessary
private:
std::vector<const char*> m_x;
};
int main() {
const char* x1[3] = {"one","two","three" };
const char* x2[2] = {"eins","zwei" };
// Variant 1
Menu m1(x1, 3);
m1.print();
// Variant 2
Menu2 m2(x2, 2);
m2.print();
}
I have a derived class called Mystring that is derived from std::string, and I would like to set the value of the string that I am working with.
From my understanding to access the string object from std::string I would use *this to get the string that I am currently working with.
I would like to set *this to a string of my choosing, I did this my setting *this = n; but it crashes my code and returns a "Thread 1: EXC_BAD_ACCESS (code=2, address=0x7ffeef3ffff8)" my code is below:
So my question is, how can I set the value of std::string to something through my derived class. Much thanks!
class Mystring : public std::string
{
public:
Mystring(std::string n);
std::string removePunctuation();
std::string toLower();
};
Mystring::Mystring(std::string n)
{
*this = n;
}
std::string Mystring::removePunctuation()
{
long int L = length();
char *cstr = new char[L + 1];
strcpy(cstr, c_str());
//cout << cstr[L-1] << endl; // last character of c string
if(!isalpha(cstr[L-1]))
{
pop_back() ;
}
return *this;
}
std::string Mystring::toLower()
{
long int L = length();
char *cstr = new char[L + 1];
strcpy(cstr, c_str());
for(int i = 0; i < L;i++)
{
int buffer = cstr[i];
cstr[i] = tolower(buffer);
std::cout << cstr[i];
}
std::string returnstring(cstr);
delete [] cstr;
return returnstring;
}
int main() {
Mystring temp("dog");
std::cout << "Hello World";
return 0;
}
Style aside, the fundamental idea of using an assignment operator to "reset" an inherited subobject is not necessarily incorrect.
However, a conversion is required to get from std::string (the type of the RHS) to Mystring (the type of the LHS, i.e. *this). The only way to perform that conversion is to use the constructor Mystring(std::string). Except… you're already in it. Hence that function is effectively recursive and will repeat forever until you exhaust your stack.
You need to upcast *this to a std::string in order to make this work:
static_cast<std::string&>(*this) = n;
I do agree with the other people here that you shouldn't be deriving from std::string, and certainly not just to add a couple of utility functions that ought to be free functions taking std::string (perhaps in a nice namespace, though?).
Don't do it. Derivation provides no benefit in this situation.
Create your added functions as free functions that operate on a string. For example:
void remove_punctuation(std::string &s) {
if (!std::isalpha(s.back()))
s.pop_back();
}
void tolower(std::string &s) {
for (auto &c : s)
c = std::tolower(c);
}
Making either/both of these a member function serves no purpose and provides no benefit.
References
GOTW #84: Monoliths Unstrung
How Non-Member Functions Improve Encapsulation
I am learning C++ in order to create a custom function (user defined function is how cloudera call it) that I want to use in Hadoop Cloudera Impala SQLs. Cloudera have provided a header file that has type definitions for custom function arguments
struct AnyVal {
bool is_null;
AnyVal(bool is_null = false) : is_null(is_null) {}
};
//Integer Value
struct IntVal : public AnyVal {
int32_t val;
IntVal(int32_t val = 0) : val(val) { }
static IntVal null() {
IntVal result;
result.is_null = true;
return result;
}
}
//String Value
struct StringVal : public AnyVal {
static const int MAX_LENGTH = (1 << 30);
int len;
uint8_t* ptr;
/// Construct a StringVal from ptr/len. Note: this does not make a copy of ptr
/// so the buffer must exist as long as this StringVal does.
StringVal(uint8_t* ptr = NULL, int len = 0) : len(len), ptr(ptr) {
assert(len >= 0);
};
/// Construct a StringVal from NULL-terminated c-string. Note: this does not make a copy of ptr so the underlying string must exist as long as this StringVal does.
StringVal(const char* ptr) : len(strlen(ptr)), ptr((uint8_t*)ptr) {}
static StringVal null() {
StringVal sv;
sv.is_null = true;
return sv;
}
}
Now for a simple Add function like the one below I understood how to pass the reference of IntVal object after setting IntVal.val and it worked !
IntVal AddUdf(FunctionContext* context, const IntVal& arg1, const IntVal& arg2) {
if (arg1.is_null || arg2.is_null) return IntVal::null();
return IntVal(arg1.val + arg2.val);
}
int main() {
impala_udf::FunctionContext *FunctionContext_t ;
IntVal num1, num2 , res;
num1.val=10;
num2.val=20;
IntVal& num1_ref = num1;
IntVal& num2_ref = num2;
res = AddUdf(FunctionContext_t, num1_ref, num2_ref);
cout << "Addition Result = " << res.val << "\n";
}
But I don't know how to do similar thing for a string function as StringVal requires me to pass pointer of uint8_t type for a string? I tried below one but then received "error: cannot convert std::string to uint8_t* in assignment"*
int main() {
impala_udf::FunctionContext *FunctionContext_t ;
StringVal str , res;
string input="Hello";
str.len=input.length();
str.ptr=&input;
StringVal& arg1=str;
res = StripVowels(FunctionContext_t, str);
cout << "Result = " << (char *) res.ptr<< "\n";
}
I also tried the following but no joy. Any pointer in the right direction will be much appreciated. Thanks.
str.ptr=reinterpret_cast<uint8_t*>(&input);
String itself is not a character pointer (which is what you need), but you can get one by using the c_str function.
str.ptr=(uint8_t*)(input.c_str ());
If you want to use new-style casts you might need both a const_cast (to cast from const char * to char *) and a reinterpret_cast, depending on how str.ptr is defined.
That's because you need a pointer to c-string, and you provide a pointer to std::string. str.ptr = input.c_str() should work for you.
EDIT:
However, it seems you need a non-const pointer. In this case you need to allocate input variable yourself, like:
char input[128];
This creates a fixed size array on the stack.
But you might want to allocate it dynamically with new:
char* input = new char[size];
Also check out functions in the cstring header, you might want to use those.
You might also need to cast it to uint8_t* as described above.
Don't forget to delete[] the string later when you don't need it anymore. But since you pass it to a function, this function should probably handle this.
I have to replace some char array code. So say I have a class that has an std::string member variable.
class foo
{
private:
std::string _sBar;
public:
const char* getBar() const { return _sBar.c_str(); }
};
existing code expects that const char*'s are returned in the string accessor functions, so I can't return a const std::string reference.
But isn't there some rule that when the stack unwinds that you can no longer trust the return value from the _sBar.c_str() ?
Yes, that's correct. Better if you ask the caller to supply a buffer with a fixed size say, the caller allocates as:
const int MAX = 1000; // choose some suitable value
char buff[MAX];
And the caller has a foo object,
foo a;
...
a.getBar(buff, MAX);
...
And you define getBar as:
void getBar(char *buffer, int size) const {
strncpy(buffer, _sBar.c_str(), size -1);
buffer[size -1] = 0;
}
you can make a copy of that string with new [ ] operator inside your member function, and it will be stored independently from the class object. In the class:
plublic:
const char* getBar() const {
char * str = new char[_sBar.length()+1];
strcpy(str, _sBar.c_str());
return str;}
In main:
foo object;
///some code
const char* bar = object.getBar();
///some code
delete [] bar;
Note it's good style to free the memory using delete [ ].
I'm confused about char * and const char *. In my example I'm not sure how to put them together. I have several const char * strings I would like to concatenate to a final const char * string.
struct MyException : public std::exception
{
const char *source;
int number;
const char *cause;
MyException(const char *s, int n)
: source(s), number(n) {}
MyException(const char *s, const char *c)
: source(s), number(0), cause(c) {}
const char *what() const throw()
{
if (number != 0) {
char buffer[1024];
// why does this not work?
cause = strerror_r(number, buffer, 1024);
}
// how to concatenate the strings?
return source + ": " + cause;
}
};
You can store a std::string and still return a const char * from your what function.
struct MyException : public std::exception
{
private:
std::string message;
public:
MyException(const char *s, int n) {
char buffer[1024];
strerror_r(n, buffer, 1024);
message.reserve(strlen(s) + 2 + strlen(buffer));
message = s;
message += ": ";
message += buffer;
}
MyException(const char *s, const char *c) {
message.reserve(strlen(s) + 2 + strlen(c));
message = s;
message += ": ";
message += c;
}
const char *what() const throw()
{
return message.c_str();
}
};
Just use strcat() and strcpy() function from string.h.
http://www.cplusplus.com/reference/clibrary/cstring/strcat/
http://www.cplusplus.com/reference/clibrary/cstring/strcpy/
Also, since you don't have to modify original strings, the difference between const char* and char* doesn't matter.
Also don't forget to malloc() (reserve the space for) the required size of destination string.
This is how I'd implement this:
struct MyException : public std::exception
{
public:
const char *source;
int number;
const char *cause;
private:
char buffer[1024]; // #1
std::string message; // #2
std::string build_message() {
if (number != 0) {
cause = strerror_r(number, buffer, 1024); // use the member buffer
}
std::string s; // #3
s.reserve(strlen(source) + 2 + strlen(cause));
return s + source + ": " + cause;
}
public:
MyException(const char *s, int n)
: source(s), number(n), cause(), message(build_message()) {}
MyException(const char *s, const char *c)
: source(s), number(0), cause(c), message(build_message()) {}
const char *what() const throw()
{
return message.c_str(); // #4
}
};
Things to note:
The original code was using a local variable for a buffer. That is a bad idea, as the pointer stored in cause would be invalid the moment the scope ends.
For the concatenated message, dynamic allocation would be required. And that also means that cleanup of that storage would be required. I grabbed an existing tool that does that and provides string-like operations: std::string.
With std::string concatenation can be done with the + operator. Note how I asked it to reserve memory for the expected size. This is memory an optimization, and is not required: the string would allocate enough memory either way.
what cannot throw an exception, otherwise a call std::unexpected would arise. So the string cannot be allocated here.
If you must work with char* pointers, you will want to use strcat. strcat takes two arguments a char* and a const char* and appends the string pointed to by the const char* onto the char*. This means you first need to copy your first string over.
You'll want to do something like this:
char* Concatenate(const char* first, const char* second)
{
char* mixed = new char[strlen(first) + strlen(second) + 2 /* for the ': ' */ + 1 /* for the NULL */];
strcpy(mixed, first);
strcat(mixed, ": ");
strcat(mixed, second);
return mixed;
}
Isn't that just ugly? And, remember, because you've dynamically allocated the char* returned by that function the caller must remember to delete[] it. This ugliness and the need to ensure the caller cleans up in the right way is why you're better off using a string implementation such as std::string.
Allocate a buffer of size strlen(source) + strlen(cause) + 3 and use sprintf to create your message. Actually you can move this code to constructor so that what becomes simple getter.
If you really must use c-strings, you should look at strcat() to concatenate them together. However, since you are creating a custom exception, it would be reasonable to consider using std::string instead because it is more friendly to use in C++.