initialize own String class by assignment in c++ - c++

I'm writing my own string class StringEx in c++ (don't worry, just for exercise) but I'm failing at creating an instance of my class by assigning a string to it:
StringEx string1 = StringEx("test"); // works fine
StringEx string2 = "test"; // doesn't work
string string3 = "test";
string1 = string3; // also works fine
I overloaded the assignment operator so it can handle with std::string but I have to create an object of StringEx first.
How can I create a new object of StringEx by assigning a string to it? Is it even possible to get c++ handling every "string" as an object of my StringEx class?
This is my StringEx.h which works now
#ifndef STRINGEX_H
#define STRINGEX_H
#include <iostream>
#include <string>
#include <vector>
using namespace std; //simplyfying for now
class StringEx
{
private:
vector<char> text;
public:
StringEx();
StringEx(string);
StringEx(const char*); // had to add this
StringEx(vector<char>);
int size() const;
int length() const;
char at(int) const;
void append(const string&);
void append(const StringEx&);
void append(const char*); // had to add this
StringEx operator+(const string&);
StringEx operator+(const StringEx&);
StringEx operator+(const char*); // had to add this too
StringEx operator=(const string&);
StringEx operator=(const StringEx&);
StringEx operator=(const char*); // had to add this too
StringEx operator+=(const string&);
StringEx operator+=(const StringEx&);
StringEx operator+=(const char*); // had to add this too
friend ostream& operator<<(ostream&, const StringEx&);
};
#endif // STRINGEX_H

A few precisions:
StringEx string1 = StringEx("test"); // works fine
This use the copy-constructor, i.e. StringEx(const StringEx& other);
StringEx string2 = "test"; // doesn't work
This tries to use a constructor with the following signature: StringEx(const char* str);
Finally, those two lines:
string string3 = "test";
string1 = string3; // also works fine
create an std::string from a const char*, which the standard library defines, and then use the copy-assignment operator overloaded for std::string that you seem to have defined correctly, i.e. StringEx& operator=(const std::string& other).
The key point here is that this statement:
Type myVar = something;
is not an assignement, it's an declaration and initialisation of a variable which uses a constructor, not the copy-assignment operator.
In your case, you're just missing a constructor that takes a const char* as parameter.

I assume that you have a non-explicit constructor of the form
StringEx (const std::string&);
or similar.
String literals are not of type std::string. "test" is of type const char[5]. std::string has a non-explicit constructor accepting a const char*, so your two calls look like this (not taking copy elision into account):
//implicitly convert "test" to temporary std::string
//construct temporary StringEx with temporary std::string
//copy-construct StringEx with temporary
StringEx string1 = StringEx("test");
//no StringEx constructor taking const char*
//two conversions necessary to make "test" into a StringEx
//invalid
StringEx string2 = "test";
An easy fix for this is to add a constructor taking a const char* to StringEx:
StringEx (const char*);
Alternatively you could just use direct initialization:
//only one conversion needed
StringEx string2 ("test");

TLDR: The compiler won't autoresolve if 2 "casts" are necessary. In this case const char* to std::string to StringEx
StringEx string1 = StringEx("test"); // works fine > This calls the constructor explicitly and therefore only one conversion must be done: const char* to std::string.
StringEx string2 = "test"; // doesn't work > This on the other hand is not clear. Do you want to cast const char* to std::string and use the StringEx(const std::string&)constructor or use some other intermediate class?
What you need is another constructor accepting const char* as parameter.
BTW:
StringEx string4 = std::string("test"); //Also works

Related

C++ code to overload the assignment operator does not work

I'm having some issues compiling the code I wrote which has a custom class with an overloaded =.
rnumber.hpp:
#include <string>
class rnumber {
public:
std::string number;
// I added this constructor in an edit
rnumber(std::string s) { number = s; }
void operator=(std::string s) { number = s; }
bool operator==(std::string s) { return number == s; }
friend std::ostream &operator<<(std::ostream &os, const rnumber n);
};
std::ostream &operator<<(std::ostream &os, const rnumber n) {
os << n.number;
return os;
}
main.cpp:
#include "rnumber.hpp"
#include <iostream>
#include <string>
int main() {
rnumber a = "123";
}
For some reason, this does not compile with the error conversion from ‘const char [4]’ to non-scalar type ‘rnumber’ requested. This shouldn't be a problem, because rnumber has an overload for =. Why am I getting this error?
EDIT: Even after adding a constructor, it doesn't work.
rnumber a = "123"; tries to initialize a with a const char*, but there is no constructor taking a const char*.
You need to implement this constructor as well:
rnumber(const char *s) { number = s; }
There are other possibilities, mentioned on other answer and comments.
The problem is that rnumber a = "123"; is initialization and not assignment so that the copy assignment operator= cannot be used.
There are 2 ways to solve this as shown below:
Method 1
Use the converting constructor rnumber::rnumber(std::string)
int main() {
rnumber a("123"); //this uses the converting ctor
}
Method 2
Create the object and then do the assignment:
int main() {
rnumber a("somestring"); //creates the object using converting ctor
a = "123"; //uses assignment operator
}
"abc" is not a std::string literal, it is a string literal, in particular it is a const char[4]. There is an implicit conversion from const char[4] to std::string, by one of it's constructors, and an implicit conversion from std::string to rnumber by the constructor you edited in, but you only get one implicit constructor per initialisation.
What you can do is explicitly construct the rnumber, and the std::string parameter can be implicitly constructed.
int main() {
rnumber a { "123" };
}
The problem's that I was using the wrong constructor.
rnumber(std::string s) { number = s; } is an std::string constructor, where the assignment rnumber a = "123"; is assigning a const char* to a.
Add another constructor rnumber(const char *s) { number = s; } to the header.

strcpy not working with string pointer in a class

I'm new to c++.I made a simple program that name members of class based on your input.
But for some reason, compiler shows this error - 'str': is not a member of 'std::basic_string<char,std::char_traits<char>,std::allocator<char>>', I have trouble understanding what it means.
Please help me out
here is my code -
#include <iostream>
#include <string>
#include <vector>
class mystring
{
private:
std::string *str;
public:
//constructors
mystring();
mystring(const std::string &strthing);
~mystring();
//methods
void display() const;
};
mystring::mystring() //defalt constructor
:str(nullptr)
{}
mystring::mystring(const std::string& strthing) //copy constructor
:str(nullptr)
{
delete str;
str = new std::string;
strcpy(this->str, strthing.str);
std::cout << "overloaded\n";
}
mystring::~mystring() //destructor
{
delete [] str;
}
void mystring::display() const //display func
{
std::cout << *str;
}
int main()
{
mystring thing;
mystring object{ "samurai" };
object.display();
}
I think something is wrong with the strcpy() function in overloaded constructor.
Thanks
There is nothing wrong with strcpy, but with how you use it. std::strings can be copied with their operator=:
std::string a;
std::string b;
a = b; // copy b to a
strcpy on the other hand is for c-strings, which a std::string is not:
char* strcpy( char* dest, const char* src );
It is unclear why you have a member of type pointer to std::string. You should either use a std::string (no pointer), or if this is an exercise to write your own string class (not an easy one!) then you should probably store the data in an array of chars.
The error you get is about strthing.str. Here strthing is a std::string which has no str member. If that constructor is supposed to be a copy constructor it should take a const mystring& as parameter not a const std::string&.
This is just a typo.
mystring::mystring(const std::string& strthing)
Should be
mystring::mystring(const mystring& strthing)
You don't use strcpy to copy std::string objects. Here's your constructor rewritten correctly (it's not a copy constructor because it doesn't copy mystring objects)
mystring::mystring(const std::string& strthing) // constructor from std::string
: str(new std::string(strthing)) // allocate new string by copying from strthing
{
std::cout << "overloaded\n";
}
Because your class allocates memory (not sure why it does but it does) you do actually need to write a genuine copy constructor and assignment operator
mystring::mystring(const mystring& strthing) // copy constructor
mystring& mystring::operator=(const mystring& strthing) // assignment operator
But I'll leave that to you.
Ther is a C++ trap between C++ type std::string and C type 'char*'.
std::string can convert to char* using std::string.c_str().
And strcpy takes char* and char const * as parameters.
In moderen C++ practice, use std::string usually, std::string * is not recommended to use.
If you want to use strcpy with std::string, you have to do the conversion between char * and std::string. Below is a simple Example:
std::string dest;
std::string source = "Hello, World!";
char temp[40];
strcpy(temp, source.c_str());
dest = temp;

What is better implicit conversion through constructor or explicit function in this case?

I am creating my own class for String using C++ solely for learning purposes.
And I stuck upon the place where I should make a decision. Let me explain the matter.
I have two options of my class. I will post below only relevant pieces of the code because I do not want to distract you from my problem at hand. If in order to help me out you need more info I will gladly provide it.
Option 1
class String {
size_t _length;
char* _stringHead;
public:
String(const std::string&);
String(const char*);
String(const char);
};
String operator+(String, const String);
const bool operator==(const String, const String);
const bool operator!=(const String, const String);
const bool operator<(const String, const String);
const bool operator<=(const String, const String);
const bool operator>(const String, const String);
const bool operator>=(const String, const String);
Option 2
class String {
size_t _length;
char* _stringHead;
public:
//irrelevant part of code in Option 2
String(const std::string&);
String(const char*);
String(const char);
//irrelevant part of code in Option 2
};
String operator+(String, const String&);
const bool operator==(const String&, const String&);
const bool operator!=(const String&, const String&);
const bool operator<(const String&, const String&);
const bool operator<=(const String&, const String&);
const bool operator>(const String&, const String&);
const bool operator>=(const String&, const String&);
//for std::string
String operator+(String, const std::string&);
const bool operator==(const String&, const std::string&);
const bool operator!=(const String&, const std::string&);
const bool operator<(const String&, const std::string&);
const bool operator<=(const String&, const std::string&);
const bool operator>(const String&, const std::string&);
const bool operator>=(const String&, const std::string&);
String operator+(const std::string&, String);
const bool operator==(const std::string&, const String&);
const bool operator!=(const std::string&, const String&);
const bool operator<(const std::string&, const String&);
const bool operator<=(const std::string&, const String&);
const bool operator>(const std::string&, const String&);
const bool operator>=(const std::string&, const String&);
//for std::string
//the same goes for char* and char
...
//the same goes for char* and char
So, as you can see from the Option 1 and Option 2 specs the decision here is about whether to use implicit type conversion which is done with the help of constructors or to type each utility separately for each type with which I want my String type to work.
As far as I can see right now the benefit of using the first approach is that it is easier to implement and maintain. While the second approach may produce better performance results.
I would like to get constructive arguments which approach is better and in what scenarios and which approach hence would you use. I think that the biggest part I am interested here is whether or not the performance benefit of the second approach is reasonable.
An implicit constructor is used to create an instance of the class type when passing an instance of the parameter type to a method that expects the class type. This implicit conversion is done by calling a constructor of the class.
For example, run this code which is similar to yours:
#include <iostream>
class String {
public:
String(const std::string& s) {
std::cout << "called" << std::endl;
};
};
std::ostream& operator<< (std::ostream& stream, const String& s) {
return stream;
}
void hello(String s) {
std::cout << "Hello " << s; // Outputs "called" before "Hello ".
}
int main() {
std::string s = "world";
hello(s); // Uses the implicit conversion constructor.
}
Because a new instance of the class String has to be created every time, it is to be expected that performance will suffer slightly. But, in my opinion, not enough to outweigh the benefits: implicit conversion can greatly simplify the job of a class designer and make using a class easier.
However, keep in mind that there exist situations where team members are more likely to be surprised if a conversion happens automatically than to be helped by the existence of the conversion.
Here is such an example:
#include <iostream>
class String {
public:
String(int size) {};
};
std::ostream& operator<< (std::ostream& stream, const String& s) {
return stream;
}
void hello(String s) {
std::cout << "Hello " << s; // Prints "Hello " as no error occurs.
}
int main() {
hello(10); // It still calls the implicit conversion constructor.
}
In the code above, an error message produced by the explicit keyword could save someone a bit of time spent debugging.
Some circumstances in which implicit conversion makes sense are:
The class is cheap enough to construct that you don't care if it's implicitly constructed.
Some classes are conceptually similar to their arguments, such as std::string reflecting the same concept as the const char* it can implicitly convert from, so implicit conversion makes sense, as is the case here.
Some classes become a lot more unpleasant to use if implicit conversion is disabled. Think of having to explicitly construct a std::string every time you want to pass a string literal.
Some circumstances in which implicit conversion makes less sense are:
Construction is expensive.
Classes are conceptually very dissimilar to their arguments. Consider the example with the String and the int.
Construction may have unwanted side effects. For example, an AnsiString class should not implicitly construct from a UnicodeString, since the Unicode-to-ANSI conversion may lose information.
So, my advice in your particular case would be to use conversion because it makes sense, as your class is so similar to std::string and to minimize code duplication, but in the future, use implicit conversion with thought.

overload array operator for mystring class

I need help figuring out how to overload the array operator for a MyString class that I have to create. I already have everything else figured out, but the arrays are giving me trouble, for some reason.
Here is my header file:
#ifndef MYSTRING_H
#define MYSTRING_H
#include <iostream>
#include <cstring> // For string library functions
#include <cstdlib> // For exit() function
using namespace std;
// MyString class: An abstract data type for handling strings
class MyString
{
private:
char *str;
int len;
public:
// Default constructor.
MyString()
{
str = 0;
len = 0;
}
// Convert and copy constructors.
MyString(char *);
MyString(MyString &);
// Destructor.
~MyString()
{
if (len != 0)
delete [] str;
str = 0;
len = 0;
}
// Various member functions and operators.
int length() { return len; }
char *getValue() { return str; };
MyString operator+=(MyString &);
MyString operator+=(const char *);
MyString operator=(MyString &);
MyString operator=(const char *);
bool operator==(MyString &);
bool operator==(const char *);
bool operator!=(MyString &);
bool operator!=(const char *);
bool operator>(MyString &);
bool operator>(const char *);
bool operator<(MyString &);
bool operator<(const char *);
bool operator>=(MyString &);
bool operator>=(const char*);
bool operator<=(MyString &);
bool operator<=(const char *);
MyString operator [](MyString *);
// Overload insertion and extraction operators.
friend ostream &operator<<(ostream &, MyString &);
friend istream &operator>>(istream &, MyString &);
};
#endif
What would the body look like for MyString::operator []?
MyString MyString::operator [](MyString *)
{
... what goes here
}
The syntax for using the array operator with an object of the given class is:
MyString s("Test");
char c = s[0];
The argument to the function is an integral value.
Hence, the operator needs to be declared as:
// The non-const version allows you to change the
// content using the array operator.
char& operator [](size_t index);
// The nconst version allows you to just get the
// content using the array operator.
char operator [](size_t index) const;
MyString MyString::operator [](MyString *)
That's not how you should typically use a subscript operator.
What do you expect when you are using the [] operator? By the way you declared it, you are using a string pointer as argument, and receiving a string as return.
Usually, you pass an index type (commonly an unsigned-integer like size_t) and return the character at that position. If that's what you want, you should do something along these lines:
char& MyString::operator [](size_t position)
{
// some error handling
return str[position];
}
char MyString::operator [](size_t position) const { /* ... */ }
For overall guidelines on overloading operators, take a look at What are the basic rules and idioms for operator overloading?.
Also, I would point out that your destructor is a bit odd:
if (len != 0)
delete [] str;
str = 0;
len = 0;
Your indentation level suggests that you expect everything to happen inside the if statement, but only the first one will. That is not particularly dangerous in this case, because only the delete would suffice.
There is no problem in deleteing a null pointer, and str and len will be destroyed shortly after, so you don't have to bother resetting them.

declaration is incompatible, beginner c++

I just need a little help on this assignment. I have to redefine operators to work with strings. I'm starting with the == operator and I have it declared in my header file, however when I go to define the function in my cpp file, it says it's incompatible with the declared function. It's probably a stupid mistake, I just don't understand this sometimes.
string.h header file
#pragma once
#include <iostream>
#include <string>
using namespace std;
#define NOT_FOUND -1
// C++ String class that encapsulates an ASCII C-string
class String
{
public:
// Default constructor
String();
// MUST HAVE: Copy-constructor that performs deep copy
String(const String& source);
// Init-constructor to initialize this String with a C-string
String(const char* text);
// Init constructor, allocates this String to hold the size characters
String(int size);
// Destructor
~String();
bool& compareTo(const String& cmp1);
// Assignment operator to perform deep copy
String& operator = (const String& source);
// Assignment operator to assign a C-string to this String
String& operator = (const char* text);
// Returns a reference to a single character from this String
char& operator [] (int index) const;
// Comparison operators
bool operator == (const String& compareTo) const;
string.cpp file
#include "string.h"
#include <string>
#include <sstream>
using namespace std;
// Default constructor
String::String()
{
Text = NULL;
}
// MUST HAVE: Copy-constructor that performs deep copy
String::String(const String& source)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = source;
}
// Init-constructor to initialize this String with a C-string
String::String(const char* text)
{
Text = NULL;
// Call the assignment operator to perform deep copy
*this = text;
}
// Init constructor, allocates this String to hold the size characters
String::String(int size)
{
Text = new char[size];
}
// Destructor
String::~String()
{
delete[] Text;
}
// Assignment operator to perform deep copy
String& String::operator = (const String& source)
{
// Call the other assigment operator to perform deep copy
*this = source.Text;
return *this;
}
// Assignment operator to assign a C-string to this String
String& String::operator = (const char* text)
{
// Ddispose of old Text
delete[] Text;
// +1 accounts for NULL-terminator
int trueLength = GetLength(text) + 1;
// Dynamically allocate characters on heap
Text = new char[trueLength];
// Copy all characters from source to Text; +1 accounts for NULL-terminator
for ( int i = 0; i < trueLength; i++ )
Text[i] = text[i];
return *this;
}
***bool& String::operator ==(string cmp2)***
{
};
Your compareTo declaration has const while definition has no const, which means they have definition has different signature with declaration:
bool& compareTo(const String& cmp1);
^^^
bool& String::compareTo(string cmp2)
{
};
BTW, why does your compareTo return bool& ?
Also should avoid using namespace std; in any header files. see why-is-using-namespace-std-considered-a-bad-practice-in-c