Home > c# > Static class member destruction in C++

Static class member destruction in C++

August 16Hits:2
Advertisement

I have a basic cache set up. Whenever a user requests a bitmap, it fetches or loads it from disk if it isn't already loaded, significantly reducing load times.

Currently, the design explicitly tells the user that they are responsible for freeing the individual bitmaps when they are done. That's fine, but the biggest problem I notice right away is that the cache is created on first use. There's no way to delete it without having the user expressly call a Release method (which seems like a bad idea). They are forced not only to release each individual bitmap, but also the cache even though they never created it.

Should I create Initialize and Release methods strictly to new and delete the std::map?

(It's named pool in the code because the member name hasn't been updated as of this posting.)

BitmapCache.h

#ifndef CBITMAPCACHE_H #define CBITMAPCACHE_H  #include <allegro\file.h> #include <allegro\gfx.h> #include <allegro\draw.h> #include <allegro\datafile.h> #include <allegro\color.h>  #include <map> #include <string>  struct BITMAP;  class BitmapCache { public:      static BITMAP* GetBitmap(std::string filename);     static BITMAP* GetBitmap(BITMAP* file);     static std::string GetBitmapFilename(BITMAP* file);     static BITMAP* GetBlankBitmap(int width, int height);  protected: private:      static std::map<std::string, BITMAP*>* _pool;     static void CleanCache(); };  #endif 

BitmapCache.cpp

#include "CBitmapCache.h"  #include <algorithm>  std::map<std::string, BITMAP*>* BitmapCache::_pool = NULL;  BITMAP* BitmapCache::GetBitmap(std::string filename) {     //Return NULL if a bad filename was passed.     if(filename.empty()) return NULL;     if(exists(filename.c_str()) == false) return NULL;      //Reduce incorrect results by forcing slash equality.     filename = fix_filename_slashes(&filename[0]);      //Create pool on first use.     if(_pool == NULL) _pool = new std::map<std::string, BITMAP*>();      //Clean the pool if it's dirty.     CleanCache();      //Search for requested BITMAP.     std::map<std::string, BITMAP*>::iterator _iter = _pool->find(filename);      //If found, return it.     if(_iter != _pool->end()) return _iter->second;      //Otherwise, create it, store it, then return it.     BITMAP* result = load_bmp(filename.c_str(), NULL);     if(result == NULL) return NULL;     _pool->insert(std::pair<std::string, BITMAP*>(filename, result));     return result; }  BITMAP* BitmapCache::GetBitmap(BITMAP* file) {     if(file == NULL) return NULL;     if(_pool == NULL) new std::map<std::string, BITMAP*>();      CleanCache();      for(std::map<std::string, BITMAP*>::iterator _iter = _pool->begin(); _iter != _pool->end(); ++_iter) {         if(_iter->second != file) continue;         return _iter->second;     }     return NULL; }  std::string BitmapCache::GetBitmapFilename(BITMAP* file) {     if(file == NULL) return std::string("");     if(_pool == NULL) return std::string("");     CleanCache();     for(std::map<std::string, BITMAP*>::iterator _iter = _pool->begin(); _iter != _pool->end(); ++_iter) {         if(_iter->second != file) continue;         return _iter->first;     }     return std::string(""); }  BITMAP* BitmapCache::GetBlankBitmap(int width, int height) {      //Smallest allowed size is 1x1.     if(width < 1 || height < 1) return NULL;      //Create pool on first use.     if(_pool == NULL) _pool = new std::map<std::string, BITMAP*>();      //Cleans the cache.     CleanCache();      //Partial Sequential Search for requested BITMAP.     if(_pool->empty() == false) {         for(std::map<std::string, BITMAP*>::iterator _iter = _pool->begin(); _iter != _pool->end(); ++_iter) {             //String keys sorted in ascending order.             //If key is not empty reached non-blank section.             if(_iter->first.empty() == false) break;             if(width == _iter->second->w && height == _iter->second->h) {                 return _iter->second;             }         }     }     //Attempt to create bitmap, if failed, return NULL.     BITMAP* result = create_bitmap(width, height);     if(result == NULL) return NULL;      //Clear to black, store it, then return to caller.     clear_bitmap(result);     _pool->insert(std::pair<std::string, BITMAP*>("", result));     return result; }  void BitmapCache::CleanCache() {     //Clean the pool of any NULL bitmaps that were deleted by caller.     for(std::map<std::string, BITMAP*>::iterator _iter = _pool->begin(); _iter != _pool->end(); ++_iter) {         if(_iter->second != NULL) continue;         _pool->erase(_iter);     } } 

Answers

Ahh, the wonderful world of game design - I assume that is what you are doing. In my oppinion, the three most common solutions are (in order of my preference):

  1. Make the cache an object instead of a pointer to an object. This way it will automatically be destructed correctly when your application ends. Disadvantage: No real way to shutdown the game and 'reboot' without restarting the app, but unless you are switching libraries or graphic backends, this is also not really needed.
  2. Leave everything as it is, don't worry. I know, the destructor won't run, but upon application exit the OS will reclaim every bit of memory anyway. However, DO NOT DO THIS IF there are 'important' tasks that need to be taken care of during cache destruction, like (i don't know) writing something back to disk. Note that this is only viable if you want to discard the complete cache object (not only its contents) only upon process termination, or else you are going to have serious memory leaks. Also do not do this if the application needs to continue execution for a long time after the your game has quit.
  3. In case neither 1) or 2) are viable, you are probably in the need to shut down your 'game engine' and start it back up again without terminating your app. In this case, however you will have some method anyway to 'shut down everything'/'close engine'/'reinit'/call it whatever you want. Then simply, this method can take care of releasing your cache.

Note to solution 2: This 'solution' is dirty and if you are a good coder you really should properly destruct all objects even upon application exit, just like I always do. But to be honest, in this particular case you have almost nothing to gain from it. The only thing that I can think of is, if you are using a memory leak detector, properly terminating everything will spare you thousands of false warnings (which IS important IF you are using a leak detector). If you are not sure, forget about option 2) and do it right.

You initialization is lazy, so there is no need for an explicit Initialize. A Release Method would be one option to clean the memory after usage and setting pool to NULL again. I guess your code is intended for single thread only so there are no real problems. Another option would be to use instances a your (then modified) class. In that way you give the user the control to use different Caches for different usages. The Cache deletes if it goes out of scope (local use) or if the user explicitly wants to.

Personally I think it's rarely a good design to use Classes in a static way. If you find it neccessary to limit the number of instances, why not making it a Singleton instance?

BTW I'd suggest a typedef for std::map<std::string, BITMAP*> and std::pair<std::string, BITMAP*> making the code a bit more readable.

If I'm understanding what you're doing correctly, it seems like the callers should get a shared_ptr to the bitmap, while the cache keeps a weak_ref. That way, bitmaps will only exist while you are using them, and the cache will know when a bitmap stops being used (the weak_ref becomes invalid). However, this does lead to the overhead of shared_ptr, which may be unacceptable in a game.

EDIT:

I'm not sure why you'd new and delete the map; however, you could make people Release() each individual bitmap, and the map would then check for that while cleaning. This will incur very little overhead (one bool, plus a check and occasional delete when searching).

In any case, just letting it be and never unloading bitmaps sounds like a very bad option.

Related Articles

  • Static class member destruction in C++August 16

    I have a basic cache set up. Whenever a user requests a bitmap, it fetches or loads it from disk if it isn't already loaded, significantly reducing load times. Currently, the design explicitly tells the user that they are responsible for freeing the

  • Incorrect access to static class memberJanuary 19

    I have following code class DataMapperFactoryBeta { static private $configClassName = 'ConfigBeta'; static private $client; static private $mapper = []; static public function initClient() { $className = 'Models\\DataMappers\\Clients\\'.self::$config

  • Using static non-member vs. non-static member variables in C++February 10

    Let's say that I have a function that gets called periodically. The function gets a value as parameter and I want to compare it to the value that was received during the earlier function call, ie. the value needs to be memorized. Should I use static

  • Changing DNS Server - how to update Static assigned Member servers?October 26

    We are upgrading our Server from 2008 to 2012. In that process, We have a Windows Server 2012 acting as a new DNS, with a new IP - DNS2. Most of our servers use static IP (as well as DNS assignment) and pointing to the old DNS (DNS1) What is the corr

  • Is there 'static class member' in javascript class of ES6? May 26

    I am testing classes in ES 6 with io.js 2.xx the example below I took from Mozilla, Things are getting on tracks (OOp in JS), at least we now have direct inheritance (at syntax level) with the 'extends' directive... the problem I pose is that member

  • Is a static function equivalent to a static Func member in C#?January 26

    It looks like a static method is the same as static Func field. Am I missing something, or are they essentially interchangeable (same footprint, etc)? A static property ends up the same as the other two example, except it includes the (minimal) overh

  • How to check presence of static const member with SFINAEJanuary 29

    Suppose i have several structs with fields that are optional: struct Param1 { static const bool x = true; }; struct Param2 { }; And want to write template function template <class ParamType> bool ReturnParam(); Which should return ParamType::x if st

  • Static class member shows confusing syntax errorFebruary 14

    This is my project setup: Main.hpp #pragma once #ifndef MAIN_HPP #define MAIN_HPP #define WIN32_LEAN_AND_MEAN #include <windows.h> #include <stdio.h> #include <stdarg.h> #include <iostream> #include <string> #include <map&

  • C++ non-static data member initializers, just a little bit confusedJanuary 19

    I am a little bit confused about why the following code does what it does: class Base { public: Base() = default; Base(const Base &) =delete; Base &operator=(const Base &) = delete; Base(const char*) {} }; class Holder { public: Holder() = def

  • Thread Safe singleton in C++ using static member instance (no lazy instantiation)January 14

    I am aware of the Meyers singleton: class Singleton{ private: Singleton(); public: Singleton & GetInstance(){ static Singleton instance; return instance; } } The advantages of it is that it uses lazy evaluation but it is not garanteed to be threadsaf

  • another initialize a static c++ class member January 28

    This question already has an answer here: Initialization of Static Class members 1 answer Righty ho, so I've read through several similar questions and I still do not understand why this code does not work. I simply want to write a class with a stati

  • static member function make_shared of shared_ptrJanuary 28

    Using libc++ I find out std::shared_ptr::make_shared() static member function in public section. It is very handy when I have already defined type alias to std::shared_ptr's specialization: using T = int; using P = std::shared_ptr< T >; auto p = P::

  • C++ register pattern with static member works "sometimes"January 29

    I am trying to implement automatic class registration at runtime (I think this technique goes under the name of "register pattern"). In the following example I am storing an int in a static member vector, but the goal would be to store function

  • Accessing member from within class-level placement new overloadFebruary 1

    Is is legal to access a data member of a class from within a placement-new overload defined in that class? #include <iostream> #include <cstdlib> using namespace std; class MyClass { public: MyClass () { // Non-default constructor to make sure

  • use of static blocks in java March 10

    As far as I understood the "static initialization block" is used to set values of static field if it cannot be done in one line. But I do not understand why we need a special block for that. For example we declare a field as static (without a va

  • Java: when to use static methodsApril 19

    I am wondering when to use static methods? Say If i have a class with a few getters and setters, a method or two, and i want those methods only to be invokable on an instance object of the class. Does this mean i should use a static method? e.g Obj x

  • Are utility classes with nothing but static members an anti-pattern in C++?February 11

    The question Where should I put functions that are not related to a class has sparked some debate over whether it makes sense in C++ to combine utility functions in a class or just have them exist as free functions in a namespace. I come from a C# ba

  • C++ Static map with object June 1

    I have seemingly simple and straightforward segment of code that is a simplified version of a problem I have been having in a game I am writing. I am trying to set a static field in one class to another value from my main method. However this code wi

  • Static factory function and lifetimeSeptember 8

    I'm trying to teach myself C++ at the moment, after years of C# and other managed languages. The class in question is a Level in a game, and the idea is to instantiate it from a static factory function. The questions that I have is: Should Level::Loa

  • Are file-scope `static` variables in C as bad as `extern` global variables?August 27

    In C, you'd often/sometimes (as a matter of style) use a file-scope static variable where you'd use a private class member variable in C++. When scaling to multithreaded programs, simply adding thread_local in C11 or the long-supported extension __th

Copyright (C) 2017 ceus-now.com, All Rights Reserved. webmaster#ceus-now.com 14 q. 0.444 s.