POV-Ray : Newsgroups : povray.off-topic : Teach yourself C++ in 21 days : Re: Days 1-5 Server Time
29 Jul 2024 20:22:04 EDT (-0400)
  Re: Days 1-5  
From: Warp
Date: 20 Apr 2012 11:16:53
Message: <4f917de4@news.povray.org>
Invisible <voi### [at] devnull> wrote:
> Right. So I can declare functions that take it or return it, and I can 
> declare (but not define) variables of that type.

  Btw, a class that has only been declared but not defined (in other words,
you have only said "class X;") is called an "incomplete type" in C++
parlance.

  You can have pointers and references to an incomplete type, and you can
use it wherever a type is required (without any actual object), such as in
function declarations. In other words, you can do eg. like this:

//--------------------------------------------------------------------
class MyClass;

// Function that takes an object of type MyClass as parameter and
// returns another object of that type:
MyClass aFunction(MyClass object);
//--------------------------------------------------------------------

  (Of course if you try to *call* that function without a full class
definition, you'll get a compiler error.)

  You could also declare an extern variable of that type (although that's
a quite rare circumstance, but it's still possible). In other words:

//--------------------------------------------------------------------
class MyClass;

extern MyClass aGlobalObject;
//--------------------------------------------------------------------

  One situation where you ought to not be able to do it, but you can, due
to a quirk in the language, is deleting an object using an incomplete
type. In other words, this will compile, albeit usually with a warning:

//--------------------------------------------------------------------
class MyClass;

void foo(MyClass* objPtr)
{
    delete objPtr; // Never do this!
}
//--------------------------------------------------------------------

  The major, major problem with that is that, while the memory taken by
that object will be freed, the destructor of the object will *not* be
called. Bad things will happen. (There was some reasoning why it's
nevertheless allowed by the standard, but I don't remember now what it
was.)

  This, however, will work ok:

//--------------------------------------------------------------------
class MyClass

// An extern function that allocates an object of type MyClass and
// returns it:
std::shared_ptr<MyClass> gimmeAnObjectOfTypeMyClass();

void foo()
{
    std::shared_ptr<MyClass> ptr = gimmeAnObjectOfTypeMyClass();

    // When foo() is exited, the allocated object will be destroyed
    // properly. In other words, its destructor will be called ok.
}
//--------------------------------------------------------------------

  The implementation of gimmeAnObjectOfTypeMyClass() (in a completely
different source file) can simply be:

//--------------------------------------------------------------------
#include "FullDefinitionOfMyClass.hh"

std::shared_ptr<MyClass> gimmeAnObjectOfTypeMyClass()
{
    return new MyClass(); // Constructs and returns an object of MyClass
}
//--------------------------------------------------------------------

  In the code above where the returned object is destroyed, at no point do we
have a full definition of 'MyClass'. Nevertheless, when the std::shared_ptr
that's managing it goes out of scope, it will delete it and its destructor
will be called. It achieves this through template magic.

  (Nevertheless, you seldom need to resort to this kind of trickery in
practical code. At least not this explicitly. Don't take this as an
example of what kind of code people usually write in C++.)

-- 
                                                          - Warp


Post a reply to this message

Copyright 2003-2023 Persistence of Vision Raytracer Pty. Ltd.