I want to have a class like below:
Class Test {
Test();
~Test();
...
}
I want to be able to use following statement:
std::string str;
Test t;
str = t;
what should I do? should I override to_string? If yes it seems that it is not possible to inherit from std::string class.
Or I have to override special operator?
What about Pointer assignment? like below:
std::string str;
Test* t = new Test();
str = t;
You can provide a user-defined conversion operator to std::string:
class Test {
//...
public:
operator std::string () const {
return /*something*/;
}
};
This will allow a Test object to be implicitly-converted to a std::string.
Although in C++ it is possible, it is generally not advised to inherit from standard classes, see Why should one not derive from c++ std string class? for more info.
I am wondering, what is the function of the assignment of
str = t;
str is a std::string type, t is Test type. So what is the expected value of the assigment? No one can guess. I suggest to explicitly call a conversion method or an operator for code clarity.
This would make your example look like:
str = t.convertToString();
or the more standard way is to implement the stream operator, which makes
str << t;
(Note this example works if str is a stream type, if it is a string, you need further code, see C++ equivalent of java.toString?.)
But, if you really want it to work without a conversion method, you can override the string assignment operator of Test:
class Test {
public:
operator std::string() const { return "Hi"; }
}
See also toString override in C++
Although this is a perfect solution to your question, it may come with unforeseen problems on the long run, as detailed in the linked article.
Related
Im making a class that is supposed to be able to assign c-strings the same way the string class i able to:
string a = "My string";
The issue I'm having is that it seams like it is not the operator=( char operand ) that is used for this purpose. So my question is this: What is used instead?
What I have:
class exstring
{
...
public:
exstring& operator=( char* );
...
};
...
int main()
{
exstring test = "test";
}
Which gives:
main.cpp:9:22: error: conversion from ‘const char [19]’ to non-scalar type ‘std::exstring’ requested
Any ideas?
You are not calling your operator = here. You need to learn the difference between assignment and initialization. What you're doing is initialization and you need a constructor that takes the parameter you're providing. In other words:
extring test = "test";
Is exactly the same as:
extring test("test");
Except that in the latter case the constructor could be explicit, but not in the former.
Ok, I know that operator. is not overloadable but I need something like that.
I have a class MyClass that indirectly embeds (has a) a std::string and need to behave exactly like a std::string but unfortunately cannot extend std::string.
Any idea how to achieve that ?
Edit: I want that the lines below to compile fine
MyClass strHello1("Hello word");
std::string strValue = strHello1;
const char* charValue = strHello1.c_str();
As per your later edit, that:
MyClass strHello1("Hello word");
std::string strValue = strHello1;
const charValue = strHello1.c_str();
should compile fine, the only solution is to write a wrapper over std::string:
class MyClass
{
private:
std::string _str;
public:
MyClass(const char*);
//... all other constructors of string
operator std::string& ();
//... all other operators of string
const char* c_str();
//... all other methods of string
friend std::string& operator + ( std::string str, Foo f);
};
//+ operator, you can add 2 strings
std::string& operator + ( std::string str, Foo f);
This however is tedious work and must be tested thoroughly. Best of luck!
You can overload the conversion operator to implicitly convert it to a std::string:
class MyClass
{
std::string m_string;
public:
operator std::string& ()
{
return m_string;
}
};
If you're not interested in polymorphic deletion of a string*, you can derived from std::string, just getting the behavior of your class behaving "as-a" std::string. No more, no less.
The "you cannot derived from classes that don't have a virtual destructor" litany (if that's the reason you say you cannot extend it) it like the "dont use goto", "dont'use do-while" "don't multiple return" "don't use this or that feature".
All good recommendation from and for people that don't know what they are doing.
std::string doesn't have a virtual destructor so don't assign new yourcalss to a std::string*. That's it.
Just make your class not having a virtual destructor itself and leave in peace.
Embedding a string and rewriting all its interface just to avoid inheritance is simply stupid.
Like it is stupid writing dozens of nested if to avoid a multiple return, just as its stupid introduce dozens of state flags to avoid a goto or a break.
There are cases where the commonly considered weird things must be used. That's why those things exist.
You can extend an interface without publically deriving from it. This prevents the case of problematic polymorphic destruction (aka no virtual destructor):
#include <iostream>
#include <string>
using namespace std;
class MyClass
: private std::string
{
public:
MyClass(const char* in)
: std::string(in)
{}
// expose any parts of the interface you need
// as publically accessible
using std::string::c_str;
using std::string::operator+=;
};
int main()
{
MyClass c("Hello World");
cout << c.c_str() << "\n";
c += "!";
cout << c.c_str() << "\n";
// Using private inheritance prevents casting, so
// it's not possible (easily, that is) to get a
// base class pointer
// MyClass* c2 = new MyClass("Doesn't work");
// this next line will give a compile error
// std::string* pstr = static_cast<MyClass*>(c2);
// delete c2;
}
Trying to sum-up all the discussions, it looks like you will never find a suitable dolution because ... your requirements are in cotraddiction.
You want you class to behave as ats::string
std::string behave as a value but
a value does not "change" when accessed but (think to c = a+b: do you expect a and b to change their value ??) ...
when accessing your "value" you want to change it.
If what I summed up collecting all the peaces (suggestion: edit your question otherwise all the answerer will risk to be lost) is correct you are in trouble with 3 & 4 (note the 3. derives from the concept of "assignment", and you can do noting to change it, while 4. comes from you directly)
Now, I can try to reverse-engineer your psychology (because you didn't provide good information about what your class represent and why you cannot "extend" a string) I can find
two possibility neither of which will fit all the above requirements.
Store a string into your class and make your class to behave as a std::string. There are threee ways to come to this point:
a. embed a string and make your class to decay to it:
essentially your class must contain
.
operator std::string&() { return my_string; } //will allow l-value assignments
operator const std::string&() const { return my_string; } //will allow r-value usage
const char* c_str() const { return my_string.c_str(); }
b. derive from std::string. In fact that's like having an anonymous my_string in it, which "decay" operations implicit. Not that different as above.
c. embed or privately derive, and rewrite the std::string interface delegating the std::string functions. It's just a long typing to get exactly the b. result. so what the f..k? (this last question is dedicated to all the ones that believe a. or b. will break encapsulation. c. will break it as well, it will only take longer!)
Don't store a string, but just get it as a result from a calculation (the "lookup" you talk about, not clarifying what it is).
In this last case, what you need is a class that decay automatically into std::string as a value.
So you need, in your class
operator std::string() const { return your_lookup_here; }
note that const is necessary, but there is no need to modify your class inner state, since the resulting string is not stored.
But this last solution has a problem with const char*: since the string is temporary, until you don't assign it, its buffer is temporary as well (will be destroyed) so decaying into const char* to assign the pointer is clueless (the pointer will point to a dead buffer).
You can have a
const char* c_str() const { return std::string(*this).c_str(); } //will call the previous one
but you can use this only into expressions or a pass-through parameter in function calls (since the temporary will exist until the evaluating expression is fully evaluated), not in an assignment towards an l-value (like const char* p; p = myclassinstace.c_str(); )
In all of the above cases (all 1.) you also need:
myclass() {}
myclass(const std::string& s) :my_string(s) { ... }
myclass(const char* s) :my_string(s) { ... }
or - in C++11, just
template<class ...Args>
myclass(Args&&... args) :my_string(std::forward<Args...>(args...)) {}
In case 2., instead of initialize the not existent my_sting, you should use the arguments to set what you will look up (we don't know what it is, since you did not tell us)
Hope in all these option you can find something useful.
You can add an operator const reference to string, as follows:
class C
{
public:
// Implicit conversion to const reference to string (not advised)
operator const std::string &() const
{
return s_;
}
// Explicit conversion to a const reference to string (much better)
const std::string &AsString() const
{
return s_;
}
private:
std::string s_;
};
As you can see from the comments, it would be much better to add an explicit conversion function if you really need this behavior. You can read about why this is bad in the Programming in C++ Rules and Recommendations article.
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 );
How do I make something like user-defined __repr__ in Python?
Let's say I have an object1 of SomeClass, let's say I have a function void function1(std::string). Is there a way to define something (function, method, ...) to make compiler cast class SomeClass to std::string upon call of function1(object1)?
(I know that I can use stringstream buffer and operator <<, but I'd like to find a way without an intermediary operation like that)
Define a conversion operator:
class SomeClass {
public:
operator std::string () const {
return "SomeClassStringRepresentation";
}
};
Note that this will work not only in function calls, but in any context the compiler would try to match the type with std::string - in initializations and assignments, operators, etc. So be careful with that, as it is all too easy to make the code hard to read with many implicit conversions.
Use a conversion operator. Like this:
class SomeClass {
public:
operator string() const { //implement code that will produce an instance of string and return it here}
};
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.)