I just miss some functions in the c++ standard library string class, so I just wanted to add these by myself. I wrote this:
#include <string>
class String : public std::string
{
public:
// some new fancy functions
};
and later noticed by reading some forums, that it is a bad idea to inherit from std::string and any other container from the standard library.
I just want the normal string, but with additional functions written by myself, how can I achieve this in the right way? Or is there no way to do it right and I have to right my own string class?
tl;dr: Use freestanding functions.
First of all - std::string is kind of a mess, and has too many methods as-is. It's bad design to lump functionality into a class which doesn't need to be in that class, and can easily be implemented using simpler, more basic class methods - as a freestanding function.
Moreover - std::string is at the same time unwieldy to manipulate (it's not a string buffer or an std::stringstream), and not impossible to manipulate, i.e. not immutable.
But coming back to my earlier point: The "right way" - if there is any - to do what you wanted is with freestanding functions. For example, suppose you want to randomly permute the contents of an std::string. Well, either:
std::string& jumble(std::string& str)
or
std::string jumble(std::string str)
or maybe, if you want to feel cool and micro-optimized,
std::string jumble(const std::string& str)
std::string jumble(std::string&& str)
depending if you want to use strings more as immutable or as mutable entities.
Also remember, that we don't really have a single std::string class - we have a template based on the character type (and an allocator etc.), so if you want to be generic, you have to accept this class:
template<
class CharT,
class Traits = std::char_traits<CharT>,
class Allocator = std::allocator<CharT>
> class basic_string;
PS - If we had uniform call syntax, like Bjarne proposed - which we really should IMHO - your freestanding functions could simply be invoked as though they were members:
auto jumbled = my_string.jumble();
Related
I want to add a new member function "charReplace" to the string class. The function will replace all the occurances of one character with another character. So I prepared a sample code.
#include <iostream>
#include <string>
std::string string::charReplace(char c1, char c2) { //error in this line
while(this->find(c1) != std::string::npos) {
int c1pos = this->find(c1); //find the position of c1
this->replace(c1pos, 1, c2); //replace c1 with c2
}
return *this;
}
int main() {
std::string s = "sample string";
s.charReplace('s', 'm') /* replace all s with m */
std::cout << s << std::endl;
}
But it is not working. I am getting the following error in line 4 while compiling.
error: 'string' does not name a type
I know that it is quite easy to get the same result by creating a non-member function. But I want to do it using a member function. So, is there a way to do this in c++?
P.S. I am still new with c++. I have been using it for a few months only. So, please try to make your answer easy to understand.
you can't. this is C++, not JavaScript (where you can prototype any classes).
your options are:
inheritance
composition
free standing functions
You can't add to the std::string interface but you can do this:
#include <string>
#include <iostream>
#include <algorithm>
int main()
{
std::string s = "sample string";
std::replace(s.begin(), s.end(), 's', 'm'); /* replace all s with m */
std::cout << s << std::endl;
}
Output:
mample mtring
The error message points to this identifier
std::string string::charReplace(char c1, char c2) {
^^^^^^
that is unknown to the compiler. The compiler knows only std::string but it does not know what unqualified identifier string means.
But in any case the approach is incorrect because all members of a class shall be declared in the class definition. You may not to add new members of a class without changing its definition.
You can write a non-member function that will do the same task.
Take into account that your function implementation has a serious bug. If arguments c1 and c2 specify the same character then the function will have an infinite loop.
I would write the function the following way
std::string & charReplace( std::string &s, char c1, char c2 )
{
for ( std::string::size_type pos = 0;
( pos = s.find( c1, pos ) ) != std::string::npos;
++pos )
{
s[pos] = c2;
}
return s;
}
But I want to do it using a member function. So, is there a way to do
this in c++?
No.
However, the real question should be: Why?
std::string is bloated enough as it is. So much that Herb Sutter once wrote a GotW article about it.
In C++, it is very good practice to extend the functionality of a class through free-standing, non-friend functions. This greatly enhances encapsulation, because a free-standing, non-friend function cannot access private or protected members.
If you look at the standard library or the Boost libraries, you will find that this principle is applied a lot. The whole "STL" (= containers/algorithms/iterators) part of the standard library is based on it. The public interfaces of container classes like std::set or std::vector are not full of functions like reverse, count_if or all_of. There are free-standing functions for that:
http://en.cppreference.com/w/cpp/algorithm/reverse
http://en.cppreference.com/w/cpp/algorithm/count
http://en.cppreference.com/w/cpp/algorithm/all_any_none_of
etc...
You should follow this excellent example in C++, not work against it.
It is technically possible to derive from std::string and add member functions to the derived class. But this is bad practice. Here is a nice question with good answers on the topic, including references to books of renowned experts like Scott Meyers or Andrei Alexandrescu:
Why should one not derive from c++ std string class?
Finally, note that this kind of consideration is not unique to C++. Take Java, for example. In Java, it is best practice as well to derive only from classes which are designed to be base classes. This is even an item in Joshua Bloch's famous "Effective Java" book. In the case of Java's String class, you could not even derive from it, of course, because it is final. Chances are that if the C++ standard library was designed today, with the new features added in C++11, std::string would be final, too (even though Bjarne Stroustrup, the inventor of C++, has apparently never been a big fan of final classes, but this is another story).
You cannot (and you should not) change the std::string class (in particular, because it is standardized in C++11 and provided by its standard library).
You could subclass std::string (but this is often frown upon).
You cannot "add" a function to a already defined class.
What you can do is make a stand alone function, called charReplace
There is no way to do add methods to existing classes in C++, unless you directly modify their definition (which you are not supposed to do with standard classes).
Thus, you are limited to two options:
Subclassing: This is discouraged for standard library classes, unless you know what you are doing. (If you have to ask, you probably aren't. E.G. virtual destructors are an important point here.)
Adding free functions (or static class functions): In your case, that would be std::string charReplace(std::string& str, char c1, char c2){...};, called as charReplace(s,c1,c2). This is the only viable option here.
I want to add a new member function "charReplace" to the string class. The function will replace all the occurances of one character with another character. So I prepared a sample code.
#include <iostream>
#include <string>
std::string string::charReplace(char c1, char c2) { //error in this line
while(this->find(c1) != std::string::npos) {
int c1pos = this->find(c1); //find the position of c1
this->replace(c1pos, 1, c2); //replace c1 with c2
}
return *this;
}
int main() {
std::string s = "sample string";
s.charReplace('s', 'm') /* replace all s with m */
std::cout << s << std::endl;
}
But it is not working. I am getting the following error in line 4 while compiling.
error: 'string' does not name a type
I know that it is quite easy to get the same result by creating a non-member function. But I want to do it using a member function. So, is there a way to do this in c++?
P.S. I am still new with c++. I have been using it for a few months only. So, please try to make your answer easy to understand.
you can't. this is C++, not JavaScript (where you can prototype any classes).
your options are:
inheritance
composition
free standing functions
You can't add to the std::string interface but you can do this:
#include <string>
#include <iostream>
#include <algorithm>
int main()
{
std::string s = "sample string";
std::replace(s.begin(), s.end(), 's', 'm'); /* replace all s with m */
std::cout << s << std::endl;
}
Output:
mample mtring
The error message points to this identifier
std::string string::charReplace(char c1, char c2) {
^^^^^^
that is unknown to the compiler. The compiler knows only std::string but it does not know what unqualified identifier string means.
But in any case the approach is incorrect because all members of a class shall be declared in the class definition. You may not to add new members of a class without changing its definition.
You can write a non-member function that will do the same task.
Take into account that your function implementation has a serious bug. If arguments c1 and c2 specify the same character then the function will have an infinite loop.
I would write the function the following way
std::string & charReplace( std::string &s, char c1, char c2 )
{
for ( std::string::size_type pos = 0;
( pos = s.find( c1, pos ) ) != std::string::npos;
++pos )
{
s[pos] = c2;
}
return s;
}
But I want to do it using a member function. So, is there a way to do
this in c++?
No.
However, the real question should be: Why?
std::string is bloated enough as it is. So much that Herb Sutter once wrote a GotW article about it.
In C++, it is very good practice to extend the functionality of a class through free-standing, non-friend functions. This greatly enhances encapsulation, because a free-standing, non-friend function cannot access private or protected members.
If you look at the standard library or the Boost libraries, you will find that this principle is applied a lot. The whole "STL" (= containers/algorithms/iterators) part of the standard library is based on it. The public interfaces of container classes like std::set or std::vector are not full of functions like reverse, count_if or all_of. There are free-standing functions for that:
http://en.cppreference.com/w/cpp/algorithm/reverse
http://en.cppreference.com/w/cpp/algorithm/count
http://en.cppreference.com/w/cpp/algorithm/all_any_none_of
etc...
You should follow this excellent example in C++, not work against it.
It is technically possible to derive from std::string and add member functions to the derived class. But this is bad practice. Here is a nice question with good answers on the topic, including references to books of renowned experts like Scott Meyers or Andrei Alexandrescu:
Why should one not derive from c++ std string class?
Finally, note that this kind of consideration is not unique to C++. Take Java, for example. In Java, it is best practice as well to derive only from classes which are designed to be base classes. This is even an item in Joshua Bloch's famous "Effective Java" book. In the case of Java's String class, you could not even derive from it, of course, because it is final. Chances are that if the C++ standard library was designed today, with the new features added in C++11, std::string would be final, too (even though Bjarne Stroustrup, the inventor of C++, has apparently never been a big fan of final classes, but this is another story).
You cannot (and you should not) change the std::string class (in particular, because it is standardized in C++11 and provided by its standard library).
You could subclass std::string (but this is often frown upon).
You cannot "add" a function to a already defined class.
What you can do is make a stand alone function, called charReplace
There is no way to do add methods to existing classes in C++, unless you directly modify their definition (which you are not supposed to do with standard classes).
Thus, you are limited to two options:
Subclassing: This is discouraged for standard library classes, unless you know what you are doing. (If you have to ask, you probably aren't. E.G. virtual destructors are an important point here.)
Adding free functions (or static class functions): In your case, that would be std::string charReplace(std::string& str, char c1, char c2){...};, called as charReplace(s,c1,c2). This is the only viable option here.
First of all, I know that the std::string class has all the functions I could possibly need. This is just for testing purposes to see what I'd be able to do in the future.
Anyway,
say I had this:
class MyString : public std::string { }
How would I then, for example, use:
MyString varName = "whatever";
because surely I'd get an error because "whatever" is a std::string not a member of the MyString class?
If you understand what I mean, how would I fix this?
(and by the way, I know this is probably a really silly question, but I am curious)
Deriving from a class simply to add member functions isn't a great idea; especially a non-polymorphic class like std::string. Instead, write non-member functions to do whatever you want with the existing string class.
If you really want to do this nonetheless, you can inherit the constructors:
class MyString : public std::string {
public:
using std::string::string;
};
Now you can initialise your class in any way that you can initialise std::string, including conversion from a character array as in your example. (By the way, "whatever" isn't a std::string, it's a zero-terminated character array, const char[9].)
How can I do this way easier:
struct Parameters {
public:
int Parameter1;
std::string Parameter1;
int Parameter2;
std::string Parameter2;
}
Isn't there "var" in C++ like in .NET ? I need parameters to be able to be integers and strings.
You have the key word 'auto' in C++ but it's on C++0x and C++1x, it's the compiler which will decide the type and it can't change after the compilation.
You're probably looking for something like boost::variant: http://www.boost.org/doc/libs/1_56_0/doc/html/variant.html
You can use Union, but you will still need to know at compile time what type you are assigning. You can sort of hide this by using templates with implicit typing to assign values, but you will still have to know the appropriate type when reading the value. Not sure how useful that would be though.
You could also use polymorphism and your own (template) wrapper class in place of the built in types.
I suggest you factor out the common code and set up to use a factory.
Use of a Factory
The problem is that you don't know the type of the parameter until you parse the string. The best method is to keep everything as a string or create a Factory and use a base class. For more research, see "c++ factory design pattern example".
struct Base_Parameter
{
std::string& parameter_as_string;
virtual void extract_parameter(const std::string& parameter_string) = 0;
};
struct Int_Parameter : public Base_Parameter
{
int parameter_value;
void extract_parameter(const std::string& parameter_string)
{
std::istringstream param_stream(parameter_string);
param_stream >> parameter_value;
}
}
Your parameter "list" would be either a container of pointers to the base class (if the types are unknown) or you could have a container of the descendent struct:
struct Parameter_Container
{
std::vector<Int_Parameter> parameters;
};
As for the factory, the factory would be an object that could create parameter objects based on some criteria. It would return a pointer to the base class.
Note: Unless you are on a tightly constrained platform, such as an embedded system, don't worry about overlapping memory locations to save room.
Basically, I have a large project that uses a custom utility class c_string that inherits from std::basic_string<char>. For numerous reasons, I would like to edit this class so that
It does not derive from std::basic_string<char>
I do not have to re-implement all the functions
I do not have to touch every file that uses c_string
So I want to change from:
class c_string : public std::basic_string<char>
{
public:
typedef std::basic_string<char> Base;
c_string() : Base() {}
}
To:
class c_string
{
...
public:
...
c_string() {...}
}
So, I'm wondering if anyone has a good strategy for making this change with minimal impact.
If your class adds custom functionality (that your project needs) over std::string, then you're out of luck: you will either have to encapsulate std::string (and implement all methods to forward to std::string implementation) or inherit from std::string (inheriting from std::string is not a good idea in general).
If your class doesn't add extra functionality over std::string, then replace class c_string { ... } with typedef std::string c_string;.
There is another thing you can do and that is to change the public inheritance with private inheritance. Doing that, you will get a bunch of compile errors, because all of string's memberfunctions are now private to the clients of your class. You can then selectively make these public:
class my_string: std::string {
public:
typedef std::string base; // convenience
using base::const_iterator;
using base::begin;
using base::end;
};
You should understand private derivation not as "my_string is a std::string" but "my_string is implemented in terms of std::string". This technique avoids some of the downsides (implicit conversion, slicing etc) of deriving from a type that is not intended to be a baseclass, like std::string. Doing this conversion is easy, it's just work with little risk of breaking anything. Afterwards though, you have control over the forwarded interfaces, which makes conversion and refactoring much easier.
I can't see any way that you could avoid at least wrapping all the functions. The most straightforward way would be to have a private basic_string member, and just write wrappers that call the same function on that member.
I have nearly the same problem as zdp. Not inherit from std::string leads in a lot of code not to use methods (we have a lot like this) as void Foo(string &s_) where a string is the parameter in the function and the user puts a my_string to Foo(). In short therm: Inherit from std::string and you can send your my_string to any function which takes a my_string OR a std::string as parameter.
I would like to rewrite my my_string class but there are a lot of code with this problem in other hands and a lot of people have to change their code too.
One bad choice 14 years ago....... sh..