Reputation: 175
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.
Upvotes: 6
Views: 357
Reputation: 1416
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:
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.std::string
every time you want to pass a string literal.Some circumstances in which implicit conversion makes less sense are:
String
and the int
.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.
Upvotes: 2