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].)
Related
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();
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..
I have a lib.h, lib.cpp and test.cpp. I would like to ask what is better?
lib.h
class c
{
std::string name;
}*cc;
lib.cpp
{
std::cout << "the name is:" << name << std:: endl;
}
test.cpp
main()
{
c tst;
tst.name="diana";
}
What should I use? std::string name or std::string *name? How can i work with &name, how will my code change and which one of these 2 methods is the best one?
First, I hardly believe your code will compile while in your main you try to get access to private data member name.
About &. It is really hard to define where to start. In short std::string &name is called reference to object of type std::string. Reference is somehow an alias to some other object. The main feature is, that you always have to initialize refence to object while creating and you can't reinitialize reference to point to another object. More about this feature you can read in C++ FAQ
EDIT
Generally you can declare public, protected and private members of your class in with arbitrary ordering:
class MyClass {
//here goes private members when working with class
//and public when working with structs
public:
//here goes public members
protected:
//here goes protected
private:
//here goes private
public:
//here goes public again
};
Ordering of members declaration is actually code policy question. For example, google style guide recommends to put all public members before private.
About hiding function members (not necessary private). You actually can't hide function member declaration, but there are several ways to "hide" implementation, but I am not sure that it's the definition of hiding you are talking about. But you can check PImpl idiom. This requires understanding of pointers so I advice you to start with them first.
Small code sample for working with pointer to string:
#include <iostream>
#include <string>
class MyClass {
public:
std::string *pstr;
};
int main() {
std::string str("test");
MyClass myObj;
myObj.pstr = &str;
std::cout << myObj.pstr->c_str() << std::endl;
}
std::string & name; is a reference. You'll need to initialize it in the constructor, so it actually refers to something. What it refers to must exist as long as your c instance exists. It's not the right solution for this.
std::string name; is an actual variable, use this.
std::string &name is "only" a reference to a string (a bit like a pointer). Because the name definitely belongs to the class c, I think it would make sense to have an instance there.
References are put to good use when passing instances around (to avoid copying).
const std::string&
is reference to a std::string, it is very important to understand the implications of that with respect to the lifetime of variables. Once the referenced variable goes away, the reference is no longer valid, and this is a very common way to f*ck up for beginning C++ programmers.
YMMV, Pick up a good tutorial first, and meanwhile, don't use references unless you know why and what you're doing
Good luck
I'd use: string name; because string* name is just a pointer that needs to be given a value, and then I'd have to think of a way to clean it up later all by myself, and string& name, would be just a name that again has to be initialized.
I have a method that effectively takes a string. However, there is a very limited subset of strings I want to use. I was thinking of typedef'ing std::string as some class, and call the functions explicit. I'm not sure that would work, however. Ideas?
The usual rule still applies: the class isn't designed to be inherited from, and its destructor isn't virtual, so if you ever upcast to the std::string base class, and let the object be destroyed, your derived class' destructor won't be called.
If you can guarantee that this will never happen, go ahead.
Otherwise, you could make the std::string a member of your class, rather than a base class. or you could use private inheritance. The problem with this approach is that you'd have to re-implement the string interface for the class to be usable as a string.
Or you could just define your class to expose a getString() function which returns the internal std::string object. Then you can still pass your own class around, and the compiler will complain if you try to pass a std::string, but the internal string is accessible when you need it. That might be the best compromise.
It sounds like you want to limit the inputs (i.e. perhaps a string that only allows letters).
I would recommend using a string within a class and then wrapping the functions you want.
class limited_string
{
std::string str;
public:
limited_string(const char *data)
: str(data)
{
if (!valid(str))
throw std::runtime_exception();
}
virtual ~limited_string() {}
limited_string &operator+=(const char *data)
{
// do all your work on the side - this way you don't have to rollback
// if the newly created string is invalid
std::string newstr(str);
newstr += data;
if (!valid(newstr))
throw std::runtime_exception();
str = newstr;
}
virtual bool valid(const std::string str) = 0;
}
It sounds like you have one method which accepts a restricted string. Is a new class really necessary?
void needs_restricted_string( std::string const &str ) {
if ( ! is_restricted_string( str ) ) throw std::runtime_error( "bad str" );
…
}
Otherwise, the only thing you want to capture is whether a string satisfies the restrictions. The only difference I see between is_restricted_string() and a compile-time class is performance, in the case you have a storage structure dedicated to checked strings. Don't prematurely optimize. That said, it would also be an error to put too much functionality into your new class or to use it like a string.
class restricted_string {
std::string storage;
public:
// constructor may implement move or swap() semantics for performance
// it throws if string is no good
// not explicit, may be called for implicit conversion
restricted_string( std::string const & ) throw runtime_error;
// getter should perhaps be private with needs_restricted as friend
std::string const &str() const { return storage; }
}; // and no more functionality than that!
void needs_restricted_string( restricted_string const &str ) {
std::string const &known_restricted = str.str();
}
std::string mystr( "hello" );
needs_restricted_string( mystr );
when doing
#include <string>
class MyString : public std::string
{
public:
MyString() {}
};
But the usage below:
MyString s = "Happy day";
MyString s("Happy Day");
MyString s = (MyString)"Happy day";
neither of them works.
It seems that there's something to do with constructors/operators declaration/overridding, but can anyone help point out where may I find these resources?
Thanks.
std::string isn't designed for inheritance. It doesn't have any virtual functions (not even the destructor!), so you can't override anything. It also doesn't have a protected interface, so you gain nothing from subclassing that you couldn't get by making some standalone utility functions that take std::string.
Keep in mind that most STL implementations expect you to use std::string with copy semantics, not reference semantics, and this makes the case for adding inherited fields or overriding functions even weaker.
If you really want something like std::string with extra functions, you could consider using composition instead of inheritance, but this isn't great either. You don't have to worry about the std::string destructor not getting called properly, but you do end up having to wrap a lot of methods from std::string that you need, which is tedious. Also, your utility functions will only work with MyString when most code is going to expect std::string, so it isn't very reusable.
You're better off making some utility functions that take std::string. Or, if std::string isn't providing what you need, you should go with some other string implementation that suits your needs. Here are some possibilities that come to mind:
SGI's STL extension, std::rope. It's in GNU C++, so maybe you can rely on it.
Boost has a pretty extensive string algorithm library that lets you use concepts to specify how your string is represented.
Also see this if you must "derive" from std::string
You need to define some constructors for the different types that you want to be able to convert into your strings. These constructors can basically just hand the parameters through to the underlying std::string.
If you don't manually create them, the compiler creates a default- and a copy-constructor for you:
MyString() : std::string() { }
MyString(const MyString &other) : std::string(other) { }
To allow construction from string literals, you need a constructor that takes a const char*:
MyString(const char* other) : std::string(other) { }
A constructor that takes a const std::string& would also be useful to convert std::strings to your string type. If you want to avoid implicit conversions of normal strings, you should make it explicit:
explicit MyString(const std::string &other) : std::string(other) { }
(Edited because my original version was full of errors and I can't delete the accepted answer)
The bottom line is that you shouldn't do this. The destructor on std::string isn't virtual. This means that if you do the following:
std::vector<std::string*> s_vector;
s_vector.push_back(new MyString("Hello"));
s_vector.push_back(new std::string("World"));
const std::vector<std::string*>::iterator s_vector_end = s_vector.end();
std::vector<std::string*>::iterator s = s_vector.begin();
for (; s != s_vector_end; ++s)
{
delete *s; // Error, MyString's destructor will
// not be called, but std::string's!
}
The only way this might be safe is if you don't add members to your string. You might think that you don't need any now, but someone who isn't aware of these issue may come along later (or you, when you've forgotten this advice perhaps) and add one, and then hey presto, you have a difficult to track down memory leak.
The problem is that you need to overload the constructor that takes const char* and call the base class constructor as follows:
class MyString : public std::string {
public:
MyString() {}
MyString( const char* c ) : std::string( c ) {}
};
All three of your tests should work then.
std::string isn't intended to be inherited from. It doesn't have any virtual methods so you can't override any of its methods.
You should look into composition. Or simply creating utility functions which operate on std::strings
You're defining a ctor MyString that takes no arguments. If overrides the other ctors, so there's no ctor taking a string argument at all.
You need to ctor of one argument of type const char *, something like
MyString(const char * s): std::string(s){}
(Don't trust the syntax, look it up; I don't write C++ every day any longer.)
Check the section in the C++ FAQ Lite on ctors.
(Oops. Const char *, not string. Told you I didn't write C++ every day.)