I have this code sample in which I try to concatenate strings:
const char* NameErrorException::what() const throw()
{
return "NameError : name '" + _name + "' is not defined";
}
Why cant I create a char*
by use of +
between strings?
I have this code sample in which I try to concatenate strings:
const char* NameErrorException::what() const throw()
{
return "NameError : name '" + _name + "' is not defined";
}
Why cant I create a char*
by use of +
between strings?
"Sesame Street 3" plus 4 houses ahead means "Sesame Street 7".
but what does "Sesame Street 3" plus "Sesame Street 4" means?
it means nothing.
you can add a house-offset to a street address, but what does it even mean to add two street addresses together? it makes no sense. "Sesame Street 3" plus "Sesame Street 4" IS NOT "Sesame Street 7".
const char*
is a location of a character on the RAM. adding yet another location will not create new string. it is meaningless. just like adding two street addresses will not create a new street address (or a new street, for that example).
const char*
is (usually) C-String. it knows nothing about string concatenation, splitting, removing or replacing. it's just the location of the first character on the RAM. this is why usually, in C++ we do use std::string
. std::string
is an object with behaviour. it DOES know about actions like concatenation, splitting, removing or replacing.
A char *
is a pointer to a char. The +
operator can be used to add an offset to a pointer, not concatenate c strings.
The first part of this is simple: You can't add char *
variables with + because C++ is not Java (or Python, or C#).
You may have learnt that "const char*
represents a string in C++". This is true as far as it goes, but if you use const char*
like that, you have all the responsibility of allocating and freeing memory at the right time.
In general, you are much better off using std::string
to represent a string. It is safe to return a std::string
from your function - it is not safe to return a const char*
(who is responsible for freeing the memory)?
However, there is one exception to that rule: When you are overriding std::exception::what()
- which is defined to return a const char*
. The fix is that you need an additional (std::string
) member of NameErrorException
which you construct at the same time as you construct _name
, and which you set to the full error message, and then what
just returns _what_error.c_str()
. So the full class would look something like:
class NameErrorException : public std::runtime_error
{
std::string _name;
std::string _what_error;
std::string build_error(const std::string &name)
{
// Use std::string literals.
return "NameError : name '"s + name + "' is not defined"s;
}
public:
NameErrorException(const std::string& name)
: _name(name)
, _what_error(build_error(name))
{}
const char* what() const throw() override
{
return _what_error.c_str();
}
}
If you don't have std::string
literals, you will have to make build_error
be:
return std::string("NameError : name '") + name + "' is not defined"s;
You get the error because operator+
is not used for concatenation of c-strings. operator+
is only defined for adding index values to pointers.
You can fix this by creating an std::string
object inside the NameErrorException
object and initializing it when the exception is created (i.e. in the constructor):
class NameErrorException : public std::runtime_error {
private:
std::string _errorString; // Used to store the error message.
public:
// Constructor.
NameErrorException(const char * const name) {
_errorString.append("NameError : name '");
_errorString.append(name);
_errorString.append("' is not defined");
}
virtual const char * what() {
return _errorString.c_str();
}
};
Alternatively, you can create a second const char *
buffer that is the correct size, and use std::snprintf()
or std::strncat()
to create the string. (Remember to clean up the new buffer inside your destructor!)