Jump to content
  • Advertisement
Sign in to follow this  
noodleBowl

C++ Questions about constructors, pointers, and smart pointers

Recommended Posts

So I have some quick questions about constructors and pointers


Question on constructors:
1. I'm working on this Sprite class and in general sprites need some kind of texture in order to display to the screen. So I have my class looking like this:

class Sprite
{
public:
  Sprite(Texture* texture);
  ~Sprite();
  
  Texture* texture;
  //Other class stuff
}

Now in the current state of my Sprite class would I be correct in that I cannot use it as a member in a different class, because it has no no-arg (default) constructor? That the only way this class could be a member is if it was a pointer?

class SpriteContainer
{
public:
  SpriteContainer();
  ~SpriteContainer();
  
  Sprite singleSprite; //**BAD** not allowed because Sprite has no no-arg (default) constructor
  Sprite* singleSpritePtr; //This is allowed because its a pointer
  
  std::vector<Sprite> sprites; //**BAD** not allowed because Sprite has no no-arg (default) constructor
  std::vector<Sprite*> spites; //This is allowed because its a vector of Sprite pointers
}

I guess my really problem here is that I struggle with when a variable/class should be a pointer or not

2. How do you make an abstract class where there are no methods that should be pure virtual functions?
All the functions of the base class have there implementation, but I also do not want this class to be instantiated

//Should not be able to instantiate this class. Should be an Abstract class
class BaseClass
{
public:
  BaseClass() { x = 0; };
  virtual ~BaseClass() { };

  int x;
  void coolMethod()
  {
    ++x;
  }
}

//Can instantiate this class. coolMethod can be called from this class
class DerivedClass : public BaseClass
{
public:
  DerivedClass();
  ~DerivedClass();
}

 

Questions about smart pointers:
1. I have never used these before, but are smart pointers ok when working with COM objects?
Will the smart pointer automatically call a COM object's Release function or should I wrap my COM object and explicitly call Release in the wrapper class' destructor?

2. Lets say there is a case where I wanted to give back the user of some APIs a pointer to a resource, but I also want them to know that they don't have to worry about cleaning it up. EG There is resource loader that will clean up all loaded resources once the program shuts down. The user has to do/worry about nothing.
What type of smart pointer should I use in a case like this? Should it be a Shared pointer? Or should I make the resource a Unique pointer and then return to the user a raw pointer?

Edited by noodleBowl

Share this post


Link to post
Share on other sites
Advertisement
1 hour ago, noodleBowl said:

Now in the current state of my Sprite class would I be correct in that I cannot use it as a member in a different class, because it has no no-arg (default) constructor? That the only way this class could be a member is if it was a pointer?

No - but you need to construct it when the class that owns the sprite is constructed in the initializer list, like so.

SpriteContainer::SpriteContainer()
  : singleSprite(nullptr)
  {
  }

// or

SpriteContainer::SpriteContainer(Texture* texture)
  : singleSprite(texture)
  {
  }

 

1 hour ago, noodleBowl said:

2. How do you make an abstract class where there are no methods that should be pure virtual functions?
All the functions of the base class have there implementation, but I also do not want this class to be instantiated

You could try making the base class's constructors private, or use C++'11's "=delete" syntax to explicitly delete them.

1 hour ago, noodleBowl said:

1. I have never used these before, but are smart pointers ok when working with COM objects?
Will the smart pointer automatically call a COM object's Release function or should I wrap my COM object and explicitly call Release in the wrapper class' destructor?

Yes, in fact the Windows SDK provides smart pointers specifically for working with COM pointers.

1 hour ago, noodleBowl said:

2. Lets say there is a case where I wanted to give back the user of some APIs a pointer to a resource, but I also want them to know that they don't have to worry about cleaning it up. EG There is resource loader that will clean up all loaded resources once the program shuts down. The user has to do/worry about nothing.
What type of smart pointer should I use in a case like this? Should it be a Shared pointer? Or should I make the resource a Unique pointer and then return to the user a raw pointer?

The usual approach is to just use a raw pointer or a reference and ensure that the non-owning class never outlives the owner of the resource. If ownership is being shared, then shared_ptr for the owners and weak_ptr (if they can outlive the owners) or raw pointers (if they can't) for the non-owners. Most of the time you're not likely to want shared ownership, though.

Edited by Oberon_Command

Share this post


Link to post
Share on other sites
2 hours ago, noodleBowl said:

I guess my really problem here is that I struggle with when a variable/class should be a pointer or not

If you want to indicate another object elsewhere, the options are either a pointer (which can be NULL) or a reference (which cannot be NULL) to that other object. 

If you have an object that is part of the thing you are creating rather than located elsewhere, you can create the instance directly as part of your object, or as a local variable in your function body.

Dynamic allocation with new and delete are objects created elsewhere -- they're on the heap -- so you need a pointer to point to them.  However, modern code should typically use smart pointers rather than raw memory allocations.

2 hours ago, noodleBowl said:

How do you make an abstract class where there are no methods that should be pure virtual functions?
All the functions of the base class have there implementation, but I also do not want this class to be instantiated

Expanding on Oberon_Command's comment here.

The easiest way is to make at least one pure virtual function.  That is:  virtual SomeFunction() = 0;

If you want it to implement ALL the functions, you'll need to use the "=delete" on the automatically generated functions of constructor, copy constructor, move constructor, assignment, move assignment, and destructor.  

Going further, you should keep your virtual functions private, or rarely, protected. The only major exception (which is covered in the article) is virtual destructors where you use polymorphic destruction, and those must be public. Derived classes should override and customize behavior rather than rewrite it completely.

2 hours ago, noodleBowl said:

EG There is resource loader that will clean up all loaded resources once the program shuts down. The user has to do/worry about nothing.

Object lifetime is a major concern in game code.  The developer ALWAYS needs to worry about it.

Again expounding on Oberon_Command's answer, the easiest approach is to require that the code using it never outlives the object.  In your game this is often implemented through rules. For example, a rendering tool might have the rule:  Returns a pointer to the rendering object, the pointer expires at the beginning of the next graphics frame.  A simulation call might have the rule:  Only valid during Update(), returns a pointer to the game object, the pointer expires at the end of the Update() process.  Pointers to objects in a container may be invalidated when items are added or removed. Pointers within an object may be valid the entire time the object exists, assuming the constructor completed rather than throwing an exception.  Everything has its own rule. This type of use is by far the most common practice, and works well if you have clear ownership of objects.  One thing owns the object and the object's lifetime, other code can reference the object within the specified rules.

If you want a fire-and-forget object you can pass along a unique_ptr that properly cleans up when the caller is done. These should not refer to other objects unless you can also guarantee (either through code or through policy) that the smart pointer never outlives the object being referenced.

Yet another option is an object handle rather than a pointer.  Effectively you assign a number (or a structure with that number) which applies to all the things you create, then return that instead of a pointer.  The handle can exist for the duration of the program. Callers need to ask the system to do work using that handle, or to return a pointer to the thing in the handle, but the caller needs to respect that the handle might be invalid.  An example is a file handle in C.  You can create a file and get the file handle, such as handle 37, and manipulate that handle.  Eventually you close that file handle.  If you try to manipulate the file handle after it is closed the function will provide a suitable error rather than crash.

In large or complex games it is extremely difficult to get shared ownership working correctly.  It is easy to accidentally create circular dependencies that are leaked, or have other issues with no clear owner and no clear responsibility.  It isn't worth the headache.

Share this post


Link to post
Share on other sites

Thanks for the info on the smart pointers and the init list!

11 hours ago, Oberon_Command said:

You could try making the base class's constructors private

10 hours ago, frob said:

The easiest way is to make at least one pure virtual function.  That is:  virtual SomeFunction() = 0;

Definitely thought about this as an option

I have also seen this weird pure virtual destructor thing, where the destructor is pure virtual and body of the destructor is still defined. But I'm not sure about this... seems kind of hacky

//In header file
class BaseClass
{
  BaseClass();
  ~BaseClass() = 0;
}

//In cpp file
BaseClass::BaseClass()
{
}

BaseClass::~BaseClass()
{
}

 

Share this post


Link to post
Share on other sites
On 11/24/2017 at 11:37 AM, noodleBowl said:

I have also seen this weird pure virtual destructor thing, where the destructor is pure virtual and body of the destructor is still defined. But I'm not sure about this... seems kind of hacky

It is the idiomatic and de facto standard way of indicating a class is a non-instantiable virtual base class.  There is nothing hacky about it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!