So I tried operator overloading of [] and it just doesn't work.
I have created this class:
class String
{
private:
char* str;
public:
String(char* str) // constructor
{
this->str = str;
}
char* Val() // returns value of this.str
{
return this->str;
}
char & operator [](int index) { return this->str[index]; }
};
and I tried to use it like so
String* str = new String("example");
cout << str[2] << endl;
the expected result was a print of the letter 'a' but it won't work..
it does work though when I create an object like so:
String str("example");
cout << str[2] << endl;
any suggestions?
String* str = new String("example");
cout << str[2] << endl;
here str is a pointer, so str[2] is not calling your operator, but accesses the third object in memory from the address str which doesn't exist, thus you have Undefined Behavior.
What you need is:
cout << (*str)[2] << endl;
str is a pointer to a String, so in str[2] the compiler is treating str as an array.
You need to access the object - so deference the pointer. i.e. (*str)[2]
In the first example str is a pointer to a String object.
You need to dereference it first, and then call the operator.
Related
I wrote a little class to learn the different constructor calls
#include <iostream>
#include <cstdlib>
#include <cstring>
class String {
private:
char *str;
public:
explicit String(const char *p);
String(String&& StringObject);
String(String& stringObject);
~String();
friend std::ostream& operator<<(std::ostream& os, const String& s);
friend String operator+(const String& s1, const String& s2);
};
String::String(const char *p)
{
size_t l = strlen(p) + 1;
str = (char *)malloc(sizeof(char) * l);
if (str)
strcpy(str, p);
std::cout << "constructor call" << std::endl;
}
String::String(String& stringObject)
{
str = (char *)malloc(sizeof(char) * (strlen(stringObject.str) + 1));
strcpy(str, stringObject.str);
std::cout << "copy constructor call" << std::endl;
}
String::~String()
{
free(str);
}
String::String(String&& stringObject)
{
this->str = stringObject.str;
stringObject.str = nullptr;
std::cout << "move constructor call" << std::endl;
}
std::ostream& operator<<(std::ostream& os, const String& s)
{
return os << s.str;
}
String operator+(const String& s1, const String& s2)
{
size_t sl1 = strlen(s1.str);
size_t sl2 = strlen(s2.str);
char str[sl1 + sl2 + 1];
strcpy(str, s1.str);
strcpy(str+sl1, s2.str);
return String{str};
}
String doNothing(String obj)
{
std::cout << "output in function: " << obj << std::endl;
return obj;
}
int main()
{
String s1("text");
String s2("and more text");
std::cout << "output: " << s1 << std::endl;
String s3 = doNothing(s1+ String{" "} + s2);
String s4 = doNothing(s1);
String s5 = s1 + s4;
}
The output is
constructor call
constructor call
output: text
constructor call
constructor call
constructor call
output in function: text and more text
move constructor call
copy constructor call
output in function: text
move constructor call
constructor call
I think the constructor calls on line 4 to 6 come from the method call
String s3 = doNothing(s1+ String{" "} + s2);
Why does the method call not cause a call to the copy constructor like the second method call?
String s4 = doNothing(s1);
Maybe because s1 is a lvalue?
Can the move constructor only be called when a function returns an object or also when a reference or pointer to an object is returned?
I'd suppose that you start line counting at 1 ;-)
The case with a complex expression
We would expect the following statement to construct in principe a temporary String for String{" "}, and then a temporary string for each of the two +. Since the parameter obj is to be constructed from a temporary object, the move constructor could be used:
String s3 = doNothing(s1+ String{" "} + s2);
The idea, of the move constructor is to be able to take advantage of the fact that the original object is disposable.
The return value of the function is also a temporary value and this one is used to construct s3 with the move constructor.
But shouldn't we then have 3 constructors and two move constructors? No, because there are copy elision rules. These cause the compiler to avoid unnecessary a copies/moves, and construct directly into the target. This happens here for the parameter.
The case with the lvalue
The next statement constructs the parameter obj with the copy constructor of an lvalue s1:
String s4 = doNothing(s1);
This is less surprising. The value is in principle copied to a temporary return value, which is then used to move-construct s4 from a temporary.
But again, copy elision simplifies this to a copy constructor and a move.
Analysing what happens
You could analyse more in detail what happens, by displaying the address of the object. This is very helpful to understand what happens on what object:
class String {...};
String::String(const char *p)
{
size_t l = strlen(p) + 1;
str = new char[l];
if (str)
strcpy(str, p);
std::cout << "constructor call:" << this<<"="<<str<< std::endl;
}
String::String(const String& stringObject)
{
str = new char[ strlen(stringObject.str) + 1];
strcpy(str, stringObject.str);
std::cout << "copy constructor call:" <<this<<"="<<str<< std::endl;
}
String::~String()
{
delete[] str;
}
String::String(String&& stringObject)
{
this->str = stringObject.str;
stringObject.str = nullptr;
std::cout << "move constructor call:"<<this <<"="<<str<< std::endl;
}
String operator+(const String& s1, const String& s2)
{
size_t sl1 = strlen(s1.str);
size_t sl2 = strlen(s2.str);
char str[sl1 + sl2 + 1];
strcpy(str, s1.str);
strcpy(str+sl1, s2.str);
return String{str};
}
String doNothing(String obj)
{
std::cout << "output in function: " <<&obj<<":"<< obj << std::endl;
return obj;
}
Online demo
Unrelated advice
I highly recommend that you get rid of malloc() and free() in C++ so I used here new[] and delete[] (or new and delete if it's not about arrays). In a second step you could alsoe get rid of the strcpy()
How can I access individual elements in a std::string with pointers? Is it possible without type casting to a const char *?
#include <iostream>
#include <string>
using namespace std;
int main() {
// I'm trying to do this...
string str = "This is a string";
cout << str[2] << endl;
// ...but by doing this instead
string *p_str = &str;
cout << /* access 3rd element of str with p_str */ << endl;
return 0;
}
There are two ways:
Call the operator[] function explicitly:
std::cout << p_str->operator[](2) << '\n';
Or use the at function
std::cout << p_str->at(2) << '\n';
Both of these are almost equivalent.
Or dereference the pointer to get the object, and use normal indexing:
std::cout << (*p_str)[2] << '\n';
Either way, you need to dereference the pointer. Through the "arrow" operator -> or with the direct dereference operator * doesn't matter.
I am trying to return a C string from a function. The function is suppose to concatinate the 3 integers with commas and return the result as a char array however I'm getting garbage values. I'm assuming I'm not calling malloc correctly. Can someone advise on what's the problem?
using namespace std;
const char * createCommand(int p1, int p2, int p3){
stringstream sstm;
std::string comma = ",";
sstm << p1 << comma << p2 << comma << p3;
std::string str = sstm.str();
const char *cstr = (const char *)malloc( (str.length()+1) * sizeof (char));
cstr = str.c_str();
return cstr;
}
int main() {
const char *cstr2 = createCommand(1,0,250); //I want to return "1,0,250"
printf("char = %s\n",cstr2);
}
Since the other two answers already gave responses to the tune of dealing with the literal problem, I'm going to instead advise on what I consider a pretty significant design flaw causing your problem: returning c-strings.
In the example code you're providing, the use of c-strings at all makes no sense. The following code will achieve what you intend to do with no difficulty or problematic code:
std::string createCommand(int p1, int p2, int p3){
std::stringstream sstm;
std::string comma = ",";
sstm << p1 << comma << p2 << comma << p3;
return sstm.str();
}
int main() {
std::string command = createCommand(1,0,250); //I want to return "1,0,250"
std::cout << "char = " << command << "\n";
}
Even if you're confined to using printf instead of the C++ iostreams library, this design is still better:
std::string createCommand(int p1, int p2, int p3){
std::stringstream sstm;
std::string comma = ",";
sstm << p1 << comma << p2 << comma << p3;
return sstm.str();
}
int main() {
std::string command = createCommand(1,0,250); //I want to return "1,0,250"
printf("char = %s\n", command.c_str());
}
And if you need the c-string passed to some older, C-based library, this design will still suffice. The point being, there's no reason to use malloc or interface with the underlying c-string representation except through the string itself.
Assignment operator, which works fine for std::string and other objects, cannot have an override for pointers. Therefore, the assignment
cstr = str.c_str();
leaks the memory that you have allocated, and replaces the pointer with the data from the string. Moreover, the pointer that your function returns now, points into memory that is invalidated upon exiting the function, creating an undefined behavior in addition to a leak.
To fix this problem, call std::strcpy(cstr, str.c_str()); Don't forget to call std::free on the result of the call. Edit: you should remove const from the return type of your createCommand function (WhozCraig, thank you for the comment).
Note: I assume that this is only an exercise in using malloc, that you know that using new[] is preferable, and that you wouldn't have to do any of the above if you could return std::string from the function.
You'll need to copy the string with some form of strcpy, before returning the pointer.
const char * createCommand(int p1, int p2, int p3){
stringstream sstm;
std::string comma = ",";
sstm << p1 << comma << p2 << comma << p3;
std::string str = sstm.str();
const char *cstr = (const char *)malloc( (str.length()+1) * sizeof (char));
strcpy(cstr, str.c_str());
return cstr;
}
I have a function which looks like:
void myFunc(char* myString, char* const buf, int startPos){
myString = &buf[startPos];
std::cout << myString << std::endl; //This outputs fine
}
.
.
.
.
char* myString = 0;
.
.
myFunc(myString, buf, startPos);
std::cout << myString << std::endl; //This doesnt output anything
Why doesn't printing out the string work after I have made the function call?
When you call
myFunc(myString, buf, startPos);
myString is copied to the function parameter. Any changes to the pointer myString does not change the pointer myString in main.
Either use char **mystring in function parameter or pass myString by reference.
void myFunc(char&* myString, char* const buf, int startPos){...}
Why not make the function return the value of mystring?
char* myFunc(char* myString, char* const buf, int startPos){
myString = &buf[startPos];
std::cout << myString << std::endl; //This outputs fine
return myString;
}
Then print the value:
std::cout << myFunc(myString, buf, startPos) << std::endl;
Or, you could do:
myString = myFunc(myString, buf, startPos);
std::cout << myString << std::endl;
If you want to modify something in a function, you have to pass a pointer to that "thing"... in this case, your "thing" is a "pointer-to-char" so you need to pass in a "pointer-to-'pointer-to-char'", so:
void myFunc(char** myString, char* const buf, int startPos){
*myString = &buf[startPos];
std::cout << *myString << std::endl; //This outputs fine
}
and call it with:
myFunc(&myString, buf, startPos);
The &myString takes the address of your "pointer-to-char" which will allow the function to modify it; inside the function, you need the extra *s to dereference this address and get to the value you want to change.
I have a piece of code as follows:
char* foo(char* str1)
{
str1 = "Some other text";
cout << "String Inside Function::" << str1 << endl;
return str1;
}
int main()
{
char* str = "This is a string";
cout << "String Before Function call::" << str << endl;
foo(str);
cout<<"String After Function call::"<<str<<endl;
return EXIT_SUCCESS;
}
But the cout after my function call gives me "This is a string" even though I have changed it in my foo function. I'm confused here although I know it has got something to do with me not passing the correct address.
You are changing the pointers value and not what it points to.
// Adding & (aka pass by reference) after the char* you can modify the pointer that you passed into foo function
// but you must understand that it was another place in memory!!!
void foo(char*& str1)
{
// This string saved in global section and here you
// changed not the text in the str1 but the pointer itself
str1 = "Some other text";
cout << "String Inside Function::" << str1 << endl;
}
// Another way to change pointer itself is pass a pointer to the pointer
void foo_v2(char** str1)
{
// You should dereference pointer to pointer (* before str1)
*str1 = "Some other text";
}
// In this case you change the content at the pointer str1
// It very dangerous, you can replace content not only the memory under str1
// but even return address if string placed in stack memory.
// Such terrible things occurred if your string that copied into str1
// occupied more bytes then it can be contained in str1
// More safe way is using for example std::string
void foo2(char* str1)
{
char *s = "Some other text";
strcpy(str1, s, strlen(s));
}
int main()
{
char* str = "This is a string";
cout << "String Before Function call::" << str << endl;
foo(str);
cout<<"String After Function call::"<<str<<endl;
return EXIT_SUCCESS;
}
When you pass a pointer by copy, the function you call gets a copy of that pointer, just as passing by copy of any other type means you can't modify the original, passing by pointer works in the same way.
void f(char* copy)
{
// the copy here is modified to point to test
copy = "Test";
}
void g(char* copy)
{
g[0] = 'C';
}
int main()
{
char* p = nullptr;
// f gets a copy of p, p cannot be modified by f
f(p);
char p2[5] = "copy";
// g gets a copy of p2, the value of p2 (the pointer) cannot be modified by g
// however, g can modify what p2 points to
g(p2);
// prints "Copy" (not "copy")
cout << p2;
}
If you want to change what a pointer "points to" via a function, you have to do that in the same way that you would change any other type passed to a function. Either through a pointer (double pointer in this case) or reference (preferred).
void make_c(char** c)
{
*c = new char[10];
}
void make_c(char*& c)
{
c = new char[10];
}
int main()
{
char* t = nullptr;
// via pointer (double pointer in this case)
make_c(&t);
delete [] t;
// via reference
make_c(t);
delete [] t;
}