Why Is There No Base Class in C++

Why is there no base class in C++?

The definitive ruling is found in Stroustrup's FAQs.
In short, it doesn't convey any semantic meaning. It will have a cost. Templates are more useful for containers.

Why doesn't C++ have a universal class Object?

  • We don't need one: generic programming provides statically type safe alternatives in most cases. Other cases are handled using multiple inheritance.

  • There is no useful universal class: a truly universal carries no semantics of its own.

  • A "universal" class encourages sloppy thinking about types and interfaces and leads to excess run-time checking.

  • Using a universal base class implies cost: Objects must be heap-allocated to be polymorphic; that implies memory and access cost. Heap objects don't naturally support copy semantics. Heap objects don't support simple scoped behavior (which complicates resource management). A universal base class encourages use of dynamic_cast and other run-time checking.

no base classes of the same type as the first non-static data member

One of the "special abilities" of a standard-layout class, is that you can reinterpret_cast a pointer to a standard-layout class object to the type of its first data member, and thus obtain a pointer to the first data member. [Edit: 9.2/19] Further, a standard-layout class with non-static data members is permitted to have empty bases. As you no doubt know, most implementations put base class sub-objects at the start of complete sub-objects. This combination of restrictions effectively mandates that the empty-base-class optimization is applied to all bases of standard-layout classes.

However, as other answers have explained, all base class sub-objects and member sub-objects that are part of the same complete object must be distinct, i.e., have different addresses if they are of the same type. Classes which violate your bullet point (that have a base class that is the same type as the first member) can't have the empty-base-class optimization fully applied, and thus can't be standard-layout classes if the base classes are positioned at the start of the complete object.

So I'm pretty sure this is what it's getting at - it's trying to say "if a class has base classes, and the empty-base-class optimization can't be applied, then the class is not standard-layout".

Edit: I'm being a bit slack with terminology here - it's possible to construct cases where the empty base class optimization can't be fully applied among the base classes (for example, in your struct D), but that doesn't matter because the base classes can still start at the beginning of the object, and conceptually "overlay" the data members, similar to a union. As you say, the base sub-objects get their addresses incremented if they (or a base) would otherwise overlay another base. While it's possible for the same thing to happen to bases of standard-layout cases (if they would overlap a data member of the same type), this would break existing ABIs, and add a special case for little gain.


You're saying that this is "forbidding" a possibility - it's not really forbidding, from my point of view, it's just not granting "standard-layout" status to types that didn't have that originally anyway (classes with bases were not PODs in C++03). So it's not forbidding such types, it's just saying that they don't get the special standard-layout treatment, which they weren't guaranteed in the first place.


Regarding my assertion that non-static data member subobjects and base subobjects are distinct, see if you find this convincing:

  • 5.9/2 (relational operators on pointers) makes it clear that no two data member subobjects (at least, with the same access specifier) have the same address as one another.
  • 5.3.1/1 (the unary operator*) says "the expression to which it is applied shall be a pointer to an object type [snip] and the result is an lvalue referring to the object to which the expression points." (emphasis added) This implies that there is at most one object of a given type at a particular address, at a given time.
  • 1.8/2 "A subobject can be a member subobject (9.2), a base class subobject (Clause 10), or an array element."... I think this implies that the categories are mutually exclusive (even if their storage overlaps). Other parts of the standard pretty strongly imply that base subobjects and member subobjects are distinct (e.g. 12.6.2).
  • Steve M's citation of 10.1/4 "For each distinct occurrence of a non-virtual base class in the class lattice of the most derived class, the most derived object (1.8) shall
    contain a corresponding distinct base class subobject of that type." - I believe this means that different bases must be at different addresses, or else they would not be "distinct" objects - there would be no way to distinguish them during their common lifetime.

I don't know how convincing this is, if you don't consider footnotes as normative or sufficiently indicating intention. For what it's worth, Stroustrup explains derived classes in "The C++ Programming Language" 12.2 in terms of member objects that have compiler-supported conversion from derived to base. Indeed, at the very end of this section, he explicitly says: "Using a class as a base is equivalent to declaring an (unnamed) object of that class. Consequently, a class must be defined in order to be used as a base (section 5.7)."


Also: it seems that GCC 4.5 does not bump up the base class in this specific situation, even though it does bump up the bases where you have repeated base classes (as you showed):

#include <assert.h>
#include <iostream>

struct E {};
struct D: E { E x ; };

int main()
{
D d;
std::cerr << "&d: " << (void*)(&d) << "\n";
std::cerr << "&d.x: " << (void*)(&(d.x)) << "\n";
std::cerr << "(E*)&d: " << (void*)(E*)(&d) << "\n";
assert(reinterpret_cast<E *>(&d) == &d.x); //standard-layout requirement
}

Output (Linux x86-64, GCC 4.5.0):


&d: 0x7fffc76c9420
&d.x: 0x7fffc76c9421
(E*)&d: 0x7fffc76c9420
testLayout: testLayout.cpp:19: int main(): Assertion `reinterpret_cast(&d) == &d.x' failed.
Aborted

Why doesn't this code cast a base class into a derived class in c++?

Whenever you push an object of b into vector vec of Base Objects, you create another object from temp which is purely of type base. You might be thinking (and you are not right!) that element which is being stored in vector will be of type Base but it will be holding an object of type bbut it's not how you achieve Dynamic Polymorphism in C++.

The statements:

std::vector<Base> vec; // 1
b temp; // 2
vec.push_back(temp); // 3

The third line will create a different object to type Base by calling assignment operator of base class Base operator=(const Base& ).

Also,b* temp = (b*)&vec[i]; is an undefined behavior because you are explicitly trying to cast a pointer to object of base to it's derived class type b but it doesn't hold object of type b and hence, you may get unexpected behavior.

NOTE:

Use dynamic_cast for casting between base and derived class as it will make sure that the conversion is valid. Otherwise, it will return nullptr. However, you need to make your base class polymorphic by having at least 1 virtual function.

If the cast is successful, dynamic_cast returns a value of type new-type. If the cast fails and new-type is a pointer type, it returns a null pointer of that type. If the cast fails and new-type is a reference type, it throws an exception that matches a handler of type std::bad_cast.

SOLUTION:

Use vector of pointers to base class to achieve run-time polymorphism.

std::vector<base *> vec;
for (int i = 0; i < 5; i++) {
b *temp = new b();
vec.push_back(temp);
}

for (int i = 0; i < 5; i++) {
b* temp = dynamic_cast<b *>(vec[i]); // For dynamic cast, you need to add virtual functions to your base class
if (temp != nullptr)
std::cout << temp->d << std::endl;
}

EDIT:

Object Slicing is also a solution for your problem. Object Slicing is the right keyword for this type of problems. Here is the definition of the Object Slicing

Object slicing happens when a derived class object is assigned to a base class object, additional attributes of a derived class object are sliced off to form the base class object.

I am quoting one of the answer in the link below. See this answer and answer for the best explanation and possible solution with some code snippet. See this article, it has explained the problem when pushing object of Derived class in a vector of base class.

"Slicing" is where you assign an object of a derived class to an instance of a base class, thereby losing part of the information - some of it is "sliced" away.

For example,

class A {
int foo;
};

class B : public A {
int bar;
};

So an object of type B has two data members, foo and bar.

Then if you were to write this:

B b;
A a = b;

Then the information in b about member bar is lost in a.

Why is there no universal base class in Swift?

There are several object-oriented languages where one can define new root classes, including C++, PHP, and Objective-C, and they work fine, so this is definitely not a special thing.

There is a reason why Objective-C has a universal base class

As Sulthan mentioned, this is not true. There are multiple root classes in Objective-C, and you can define a new root class by simply not specifying a superclass. As Sulthan also mentioned, Cocoa itself has several root classes, NSObject, NSProxy, and Object (the root class of Protocol in ObjC 1.0).

The original Objective-C language was very flexible and someone could in theory come along and create his own root class and create his own framework that is completely different from Foundation, and uses methods completely different from retain, release, alloc, dealloc, etc., and could even implement a completely different way of memory management if he wanted. This flexibility is one of the things so amazing about the bare Objective-C language -- it simply provides a thin layer, all the other things like how objects are created and destroyed, memory management, etc., can all be determined by the user frameworks sitting on top.

However, with Apple's Objective-C 2.0 and modern runtime, more work needed to be done to make your own root class. And with the addition of ARC, in order to use your objects in ARC, you must implement Cocoa's memory management methods like retain and release. Also, to use your objects in Cocoa collections, your class must also implement things like isEqual: and hash.

So in modern Cocoa/Cocoa Touch development, objects generally must at least implement a basic set of methods, which are the methods in the NSObject protocol. All the root classes in Cocoa (NSObject, NSProxy) implement the NSObject protocol.

So, what's up with that? Are Swift classes with no defined
superclasses just NSObjects that pose as proper root classes under the
hood? Or is the default object-behaviour duplicated for each new
root-class? Or have they created another Swift-baseclass?

This is a good question, and you can find out by introspection with the Objective-C runtime. All objects in Swift are, in a sense, also Objective-C objects, in that they can be used with the Objective-C runtime just like objects from Objective-C. Some members of the class (the ones not marked @objc or dynamic) may not be visible to Objective-C, but otherwise all the introspection features of the Objective-C runtime work fully on objects of pure Swift classes. Classes defined in Swift look like any other class to the Objective-C runtime, except the name is mangled.

Using the Objective-C runtime, you can discover that for a class that is a root class in Swift, from the point of view of Objective-C, it actually has a superclass named SwiftObject. And this SwiftObject class implements the methods of the NSObject protocol like retain, release, isEqual:, respondsToSelector:, etc. (though it does not actually conform to the NSObject protocol). This is how you can use pure Swift objects with Cocoa APIs without problem.

From inside Swift itself, however, the compiler does not believe that a Swift root class implements these methods. So if you define a root class Foo, then if you try to call Foo().isKindOfClass(Foo.self), it will not compile it complaining that this method does not exist. But we can still use it with a trick -- recall that the compiler will let us call any Objective-C method (which the compiler has heard of) on a variable of type AnyObject, and the method lookup produces an implicitly-unwrapped optional function that succeeds or fails at runtime. So what we can do is cast to AnyObject, make sure to import Foundation or ObjectiveC (so the declaration is visible to the compiler), we can then call it, and it will work at runtime:

(Foo() as AnyObject).isKindOfClass(Foo.self)

So basically, from the Objective-C point of view, a Swift class either has an existing Objective-C class as root class (if it inherited from an Objective-C class), or has SwiftObject as root class.

C++ Why base class/struct constructor can't have more than one argument to be implicitly callable from derived?

The "magic" is that you're accidentally using aggregate initialization. In C++20 (and not before), you can invoke aggregate initialization by using parenthesis instead of curly braces, so long as no actual constructor (like the copy/move constructor) would have been invoked. This was done to allow indirect construction of types (emplace, optional's in-place constructor, make_shared/unique, etc) to work on aggregate types.

Your type Derived is an aggregate, as it has no user-provided constructors and one subobject (the base class). So your use of constructor syntax will invoke aggregate initialization, with the first aggregate member being the base class. Which can be constructed from an int (BTW, it's generally bad form to not make constructors from a single parameter explicit unless they're copy/move constructors).

Generally speaking, unless you are intending to create an aggregate (ie: the type is just an arbitrary bundle of values), derived classes ought to have constructors. I can't recall when I've written a derived class that didn't also have at least one constructor. And if it is your intent for the derived class to have the base class constructors, you should explicitly spell that out whenever you want to do that. Constructors are not inherited without explicit say-so.

Can a derived class have a constructor that is not in the base class in C++?

Yes.

Example:

class Animal
{
public:
Animal(double length, double height) : fLength(length), fHeight(height) {}
virtual ~Animal(){};

private:
double fLength;
double fHeight;
};

class Cat : public Animal
{
public:
Cat(double length, double height, double Legs) : Animal(length, height), fLegs(Legs) {}
private:
double fLegs;
};

int main(void)
{
Cat(1,2,4);
return 0;
}

Why do we need to have Object class as baseclass for all the classes?

Having a single-rooted type hierarchy can be handy in various ways. In particular, before generics came along, it was the only way that something like ArrayList would work. With generics, there's significantly less advantage to it - although it could still be useful in some situations, I suspect. EDIT: As an example, LINQ to XML's construction model is very "loose" in terms of being specified via object... but it works really well.

As for deriving from different classes - you derive directly from one class, but that will in turn derive indirectly from another one, and so on up to Object.

Note that the things which "all objects have in common" such as hash code, equality and monitors count as another design decision which I would question the wisdom of. Without a single rooted hierarchy these design decisions possibly wouldn't have been made the same way ;)

Base class of everything in c++

There is no universal base class in C++.

Exception classes should usually derive from std::exception, so that catch(const std::exception&) can be used.

catch(...) catches any exception object type (including primitive types). It can be rethrown inside the catch block with throw;:

try
{
if (something) throw int(a);
if (something) throw char(b);
if (something) throw float(c);
}
catch(...)
{
if(stillFailed) throw; // throws the same exception again
}

It is also possible to get a std::exception_ptr object which represents the thrown object (of unknown type), using std::current_exception(), inside the catch(...) block. It can then be compared for equality with other std::exception_ptr objects, or rethrown from another function using std::rethrow_exception(). See http://en.cppreference.com/w/cpp/header/exception . There is no way to get direct access to the exception object, because its type is unknown.



Related Topics



Leave a reply



Submit