You seem to have mostly arrived at the right answers, though the reasoning needs work. The key issue at play here is the question of "how to lay out the memory of an instance of C if it inherits the same base class twice?"
i) There are 2 copies of the base class B in the memory layout for an object of type C. The example provided is a case of "diamond inheritance", because when you draw out the dependency/inheritance tree, you essentially draw a diamond. The "problem" with diamond inheritance is essentially to ask how to lay the object out in memory. C++ went with two approaches, a fast one, this, duplicating the data members, and a slower one, "virtual inheritance". The reason to take the non-virtual approach is that if you inherit a class that has no data members (what would be an interface in Java), then there is no problem with "duplicating the data members", because they do not exist (see my note at the bottom). It is also advisable to use non-virtual inheritance if your plan is to only use single inheritance.
ii) If you have a virtual class C
, then that is the way of saying in the C++ language that you would like to have the compiler perform acts of heroism to ensure that only one copy of any/all base classes exist in the memory layout of your derived class; I believe this also incurs a slight performance hit. If you use any 'B' members from a 'C' instance now, it will always refer to the same place in memory. Note that virtual inheritance has no bearing on whether your functions are virtual.
Aside: This also is completely unrelated to the concept of a class being abstract. To make a class abstract in C++, set any method declaration = 0, as in void foo() = 0;
; doing so for any method (including the destructor) is sufficient to make the entire class abstract.
iii) Java outright forbids it. In Java there is only single inheritance plus the ability to implement any number of interfaces. While interfaces do grant you the "is-a" relationship and the ability to have virtual functions, they implicitly avoid the issues that C++ has with data layouts and diamond inheritance, as an interface cannot add any data members, ipso facto: there is no confusion about how to resolve any data member's location.
An important extension to iii is to realize that virtual function call dispatch is not impacted at all if you happen to "implement the same interface twice". The reason is that the method will always do the same thing, even if there were multiple copies of it in your virtual table; it only acts on the data of your class, it does not itself contain data that needs to be disambiguated.