Note that there are some explanatory texts on larger screens.

plurals
  1. POIn C++, can we upcast an array and then try to put another subtype into it (inspired by Java ArrayStoreException)?
    primarykey
    data
    text
    <p>I have tried to see what would happen in C++ if we try to "break" an array of objects in a similar way we can try to do it in Java.</p> <p>In Java we can have an array of type Double[], for example, upcast it to Number[] (because Double is a subclass of Number), and try to add another subclass of Number to array, for example, an Integer. The code would compile, but we would get ArrayStoreException in runtime, because the type Integer would be checked against the actual type of array, which happens to be Double, in runtime, and there would of course be a mismatch. The code could look like this:</p> <pre><code>Double[] ds = new Double[12]; Number[] ns = ds; ns[0] = 2.3; // OK ns[1] = new Integer(1); // compiles, but we have ArrayStoreException in runtime </code></pre> <p>So I thought - what about C++? Can we try to perform the same trick? What will happen in runtime?</p> <p>Here's the code I tried, and the output.</p> <pre><code>#include &lt;iostream&gt; class A { public: A(int v = 0): val(v) {} virtual void g() {std::cout &lt;&lt; val &lt;&lt; " in A\n";} void setVal(int v) {val = v;} protected: int val; }; class B : public A { public: virtual void g() {std::cout &lt;&lt; val &lt;&lt; " in B\n";} }; class C : public A { public: C(int v = 0): A(v) {} virtual void g() {std::cout &lt;&lt; val &lt;&lt; " in C\n";} private: int stuff[10]; }; void f(A* as) { as[1] = *(new A(12)); } void f2(A* as) { as[1] = *(new C(22)); } int main() { A* bs = new B[5]; for (int i=0 ; i&lt;5; ++i) { bs[i].setVal(i); bs[i].g(); } std::cout &lt;&lt; std::endl; f(bs); for (int i=0 ; i&lt;5; ++i) { bs[i].g(); } std::cout &lt;&lt; std::endl; f2(bs); for (int i=0 ; i&lt;5; ++i) { bs[i].g(); } } </code></pre> <p>Output:</p> <pre><code>0 in B 1 in B 2 in B 3 in B 4 in B 0 in B 12 in B 2 in B 3 in B 4 in B 0 in B 22 in B 2 in B 3 in B 4 in B </code></pre> <p>See that both creating A or C and then copying it inside the array of B does copy data (and, as expected, in case of C only the data that is part of A is copied - no memory corruption after the copied element), but the method of B is selected, meaning that vptr must have not been copied.</p> <p>So my questions are:</p> <ul> <li><p>I guess that vptr is not copied in default assignment operator. Is it so? Is it the only possible reason that we have method from B called but data from C object?</p></li> <li><p>Can we actually come up with an example that can make some bad or unexpected things happen, some runtime failure? What I mean by bad, I guess, is to have an <strong>array of Bs</strong>, but an object of A or C in it (<strong>not B or subtype of B</strong>), an object that is "alien" to B? </p></li> <li><p>Or maybe the C++ language does guarantee explicitly or implicitly by some combinations of its features that this cannot happen (like Java when it explicitly raises ArrayStoreException)?</p></li> </ul> <p><strong>UPD:</strong></p> <pre><code>A* bs = new B[5]; </code></pre> <p>I actually changed this line at the last moment, to put emphasis on runtime selection of B method (should not have done it, it is obvious because the method is virtual). I initially had <code>B* bs = new B[5];</code> and the output was the same.</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload