lfxgroove lfxgroove - 2 months ago 14
C++ Question

c++ cout and char-pointers, something I'm missing with pointers

I'm trying to write a piece of code for a kind of tree I'm making, and if a node doesn't exist i would like to throw an exception showing what node doesn't exist. Ie:

Trie t = Trie();
try {
t.get('a');
} catch(NoSuchNode e) {
cout << e.what() << endl;
}


this code should just do nothing if the node exists, otherwise print the error given, here's my code for the exception (i use the std::exception because that seemed like a good idea):

class NoSuchNode : public std::exception
{
private:
char *_node_name;
public:
NoSuchNode(const char *node_name) { _node_name = new char(*node_name); }
virtual const char* what() const throw()
{
std::stringstream ss;
ss << "There exists no node for: " << _node_name << ".";
const std::string& tmp = ss.str();
return tmp.c_str();
}
};


And this works as it should, valgrind doesn't give any errors on invalid reads either so it's fine i suppose? Now to my question, before i came up with this solution i had just changed:

_node_name = new char(*node_name);


to:

_node_name = node_name;


so that i don't need to allocate more space for it. The problem is that sometimes it doesn't really print any good data and sometimes the one char and even more after that. Not really what i would have expected. Also, valgrind complained that i read past the memory i could use. Why is this? My bet is that if std::string just get's a pointer to a char it thinks is a null-terminated string and therefore tries to read from the start of the memory until it finds the null-character? Is this correct and what's the proper way to solve this problem? Should i just pass the data by value as I'm pretty much doing?

Added:
Each node should hold just one char and nothing more, everything passed by char is just one char and nothing else. So i'm not having a string in chars so to say.

So it turns out what i was really interested in was how the streams handle char* input.
Thanks for the answers!

If something other is wrong in my code and you have the time please do tell me!

Thanks in advance!

Answer

new char(*node_name) allocates a single character corresponding to the first character of node_name. Not a string. When you use it like a NULL-terminated C string, the terminator byte is missing and you'll get garbage.

A char* is special for std::cout and other streams because it means "null-terminated C string". You'll have to dereference it (<< *_node_name <<) or, like hmjd suggests, simply use a single char (char node_name).

When you use node_name without reallocation, you run the risk that the space your pointer is referring to is already gone when you need it. You don't own the pointed-to memory, so you should either use it immediately or make a copy.

In general, writing own exception classes is not easy, and there is much to consider. In your case, just derive from std::logic_error or std::runtime_error, and let them handle the strings.