Pergunta

I'm pretty sure this is dangerous code. However, I wanted to check to see if anyone had an idea of what exactly would go wrong.

Suppose I have this class structure:

class A {
protected:
  int a;
public:
  A() { a = 0; }        
  int getA() { return a; }
  void setA(int v) { a = v; }
};

class B: public A {
protected:
  int b;
public:
  B() { b = 0; }
};

And then suppose I want to have a way of automatically extending the class like so:

class Base {
public:
   virtual ~Base() {}
};

template <typename T>
class Test: public T, public Base {};

One really important guarantee that I can make is that neither Base nor Test will have any other member variables or methods. They are essentially empty classes.

The (potentially) dangerous code is below:

int main() {
  B *b = new B();

  // dangerous part?
  // forcing Test<B> to point to to an address of type B
  Test<B> *test = static_cast<Test<B> *>(b);

  //
  A *a = dynamic_cast<A *>(test);
  a->setA(10);
  std::cout << "result: " << a->getA() << std::endl;
}

The rationale for doing something like this is I'm using a class similar to Test, but in order for it to work currently, a new instance T (i.e. Test) has necessarily be made, along with copying the instance passed. It would be really nice if I could just point Test to T's memory address.

If Base did not add a virtual destructor, and since nothing gets added by Test, I would think this code is actually okay. However, the addition of the virtual destructor makes me worried that the type info might get added to the class. If that's the case, then it would potentially cause memory access violations.

Finally, I can say this code works fine on my computer/compiler (clang), though this of course is no guarantee that it's not doing bad things to memory and/or won't completely fail on another compiler/machine.

Foi útil?

Solução

The virtual destructor Base::~Base will be called when you delete the pointer. Since B doesn't have the proper vtable (none at all in the code posted here) that won't end very well.

It only works in this case because you have a memory leak, you're never deleting test.

Outras dicas

Your code produces undefined behaviour, as it violates strict aliasing. Even if it did not, you are invoking UB, as neither B nor A are polymorphic classes, and the object pointed to is not a polymorphic class, therefore dynamic_cast cannot succeed. You're attempting to access a Base object that does not exist to determine the runtime type when using dynamic_cast.

One really important guarantee that I can make is that neither Base nor Test will have any other member variables or methods. They are essentially empty classes.

It's not important at all- it's utterly irrelevant. The Standard would have to mandate EBO for this to even begin to matter, and it doesn't.

As long as you perform no operations with Test<B>* and avoid any magic like smart pointers or automatic memory management, you should be fine.

You should be sure to look for obscured code like debug prints or logging that will inspect the object. I've had debuggers crash on me for attempting to look into a value of a pointer set up like this. I will bet this will cause you some pain, but you should be able to make it work.

I think the real problem is maintenance. How long will it be before some developer does an operation on Test<B>*?

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top