0

I have a problem when I derive a String class from std::string and I test it.

Here is the String class:

namespace Types
{

class String : public std::string
{
public:
    String();

    /*!
     * @fn String(const String &other);
     * @brief Copy contructor
     * @param other
     */
    String(const String &other)
        : std::string(other) {}
    String(const char* format_string, ...);

    virtual ~String();
};

} /* namespace Types */

And here is the test class :

class String_test : public CppUnit::TestFixture
{
    CPPUNIT_TEST_SUITE(String_test);
    CPPUNIT_TEST(testCopyConstructor);
    CPPUNIT_TEST_SUITE_END();

public:
    void setUp(void)
    {
        mTestObj = new Types::String();
    }
    void tearDown(void)
    {
        delete mTestObj;
    }

protected:
    void testCopyConstructor(void)
    {
        *mTestObj = "toto";
        std::cout << *mTestObj << std::endl;
        Types::String new_string((const Types::String&)mTestObj);
        CPPUNIT_ASSERT(true == true);
    }
private:
    Types::String *mTestObj;
};

The compilation is ok but when the program is running I have this error:

##Failure Location unknown## : Error
Test name: String_test::testCopyConstructor
uncaught exception of type std::exception (or derived).
- basic_string::_M_construct null not valid

I have searched the documentation about copy constructor and derived class, it seems that is ok in code. I don't understand what's the problem.

Anyone have an idea ?

Thanks

J Laurent
  • 13
  • 4
  • 3
    https://stackoverflow.com/questions/6006860/why-should-one-not-derive-from-c-std-string-class Deriving from string is bad practice – RoQuOTriX Mar 25 '20 at 15:54
  • You probably meant to use `*mTestObj` instead of `(const Types::String&)mTestObj`. Avoid C style casts, they let you cast all sorts of things to other things they shouldn't be casted to. If adding one fixes a compiler error, most of the time it's just hiding it from you without actually fixing anything. – François Andrieux Mar 25 '20 at 15:54
  • 1
    pubplicly inheriting from standard types is a bad idea in general. Privately inheriting is fine, but then you might as well use composition over inheritance. – 463035818_is_not_an_ai Mar 25 '20 at 16:07

1 Answers1

1

mTestObj is a pointer, so

(const Types::String&)mTestObj

cannot be interpreted as static_cast<const Types::String&>(mTestObj), because there is no constructor of Types::String that takes a Types::String*.

So it will be interpreted as

 reinterpret_cast<const Types::String&>(mTestObj)

which has the same meaning as

 *reinterpret_cast<const Types::String*>(&mTestObj)

Because there is no Types::String object at the address of mTestObj (the pointer), accessing the result of this cast as if there was causes undefined behavior. This happens in the std::string copy constructor. Alternatively the result of the cast might already be unspecified if there is alignment mismatch for Types::String.

Do not use C-style casts, as you can see they are very dangerous.

You obtain a reference from a pointer using * and you can get a const reference by calling std::as_const on that:

Types::String new_string(std::as_const(*mTestObj));

although I am not sure why you would need const in the first place, so probably really you want just

Types::String new_string(*mTestObj);
walnut
  • 21,629
  • 4
  • 23
  • 59