SDL and Copy Constructors

Started by
14 comments, last by yet_another_painter 10 years, 4 months ago

Is it common practice to include the header file more then once? In my main file i just have .h after .h listed. I always thought this way was better since I used to do it like

a.h

a.cpp

b.h

b.cpp

c.h

c.cpp

Now a days I do

a.h

b.h

c.h

with the .cpp included from the .h

If you really wanted to compile everything together in one big file, why not put just the cpp files into that one big file? Each cpp file should include all the headers it needs.

However... you can compile a bunch of cpp files separately and let the linker do the linking.

Unfortunately, I work somewhere that does "bulk builds", and I work on one particular piece of software that basically includes every relevant cpp file into one big cpp file, so my "professional" environment is almost exactly how you work, though it doesn't sound like you knew there was a choice. The main difference I guess it the build step that makes the one file that includes all the cpp files for me. After a few years of neglect, none of the cpp files have a meaningful set of headers on them any more, and static is practically meaningless as a storage type or as a way to throw a simple helper function into a file. Trust me, it's more fun to have separate compile units.

It's common practice for a header file to include what needs to be shared with other cpp files, while the cpp file includes only what it needs to compile all by itself. For example, SmartImage.cpp would include SmartImage.h, because that cpp needs to know the definition of the SmartImage class in order to compile. In your situation, it doesn't even look like you really use header files for anything, and if you got into some circular dependency situation where you really needed headers, you probably wouldn't be able to fix it without changing how you include files.

So, how do you compile your code? Depending on what tools you use, I'm sure someone here can help you set up your project.

Advertisement
Use std::unique_ptr for your SDL_Surface. This will ensure that compiler-generated copy constructor/assignment operators cannot be used. If you write movement constructor/assignment (or use a recent compiler that knows how to auto-generate them), the type will still work in a std::vector.

If you're using a recent Clang/GCC or the latest Visual Studio then this gets even easier as you can probably used defaulted movement operators. VS 2010/2011 will require you to write them manually, however.

Moving works much better than copying when a vector is resized or when elements are put into them. It avoids any of the nastiness inherent with trying to share ownership or deep copying resources.

Sean Middleditch – Game Systems Engineer – Join my team!

As a poster previously said - you gotta make sure you do SDL initializations before you try and create any copies of your class instances

because you call an SDL function in your copy constructor

As for the comment on your including the cpp - usually you would include your files in the following manner

SmartImage.h


#ifndef SMARTIMAGE_H     // Include header guards (so your declarations wont be included more than once)
#define SMARTIMAGE_H

class SmartImage
{
    public:
           // your class declarations
    private:
           // your class declarations
};

#endif

SmartImage.cpp


#include "SmartImage.h"   // Include your header file from your .cpp file - only include cpp files if there is no other way

SmartImage::SmartImage()
{
     // your code    
}

SmartImage::~SmartImage()
{
     // your code  
}

// copy constructor
SmartImage::SmartImage (const SmartImage& param)
{         
    // your code      
}

void SmartImage::load(std::string filename)
{
   // your code
}

void SmartImage::copy_surface(SDL_Surface *temp_surface)
{
     // your code
}

main.cpp


#include "SmartImage.h"     // now you can use SmartImage here

int main()
{
    // your code - make sure you initialize SDL before you make any instance copies of your image class
}

Here is my realization of your class.

[source='SmartImage.h']/*

/*
* SmartImage.h
*
* Created on: Dec 7, 2013
* Author: Bohdan Kornienko
*/
#ifndef SMARTIMAGE_H_
#define SMARTIMAGE_H_
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <string>
#include <iostream>
class SmartImage
{
private:
SDL_Surface *surface;
bool image_loaded;
SDL_Surface* load_image(std::string file_name);
public:
SmartImage();
SmartImage(const SmartImage& param);
virtual ~SmartImage();
SDL_Surface* content() const
{
return surface;
}
void load(std::string filename);
void copy_surface(SDL_Surface *target_surface);
bool is_image_loaded();
bool clean_up();
};
#endif /* SMARTIMAGE_H_ */

[/source]

[source='SmartImage.cpp']/*

/*
* SmartImage.cpp
*
* Created on: Dec 7, 2013
* Author: Bohdan Kornienko
*/
#include "SmartImage.h"
SmartImage::SmartImage()
: surface(NULL), image_loaded(false)
{
}
SmartImage::SmartImage(const SmartImage& param)
: image_loaded(true)
{
surface = SDL_DisplayFormat(param.content());
std::cout << &surface << std::endl;
}
SmartImage::~SmartImage()
{
// TODO: put something here when is need
}
SDL_Surface* SmartImage::load_image(std::string file_name)
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
loadedImage = IMG_Load(file_name.c_str());
if(loadedImage == NULL)
{
std::cerr << "Image [" << file_name << "] is not loaded\n";
image_loaded = false;
return NULL;
}
optimizedImage = SDL_DisplayFormat(loadedImage);
SDL_FreeSurface(loadedImage);
if(optimizedImage == NULL)
{
image_loaded = false;
return NULL;
}
std::cout << "Image [" << file_name << "] loaded\n";
image_loaded = true;
return optimizedImage;
}
void SmartImage::load(std::string filename)
{
if(image_loaded == false)
{
surface = load_image(filename);
}
else
{
SDL_FreeSurface(surface);
surface = load_image(filename);
}
std::cout << &surface << std::endl;
}
void SmartImage::copy_surface(SDL_Surface *temp_surface)
{
if(image_loaded == true)
{
SDL_FreeSurface(surface);
}
surface = SDL_DisplayFormat(temp_surface);
image_loaded = true;
}
bool SmartImage::is_image_loaded()
{
return image_loaded;
}
bool SmartImage::clean_up()
{
SDL_FreeSurface(surface);
image_loaded = false;
return true;
}

[/source]

And here some of using this class example:

[source='smartimg.cpp']/*

/*
* smartimg.cpp
*
* Created on: Dec 7, 2013
* Author: Bohdan Kornienko
*/
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <string>
#include "SmartImage.h"
void apply_surface(int x, int y, SDL_Surface* source, SDL_Surface* dest)
{
SDL_Rect offset;
offset.x = x;
offset.y = y;
SDL_BlitSurface(source, NULL, dest, &offset);
}
int main(int argc, char **argv)
{
if(SDL_Init(SDL_INIT_EVERYTHING) == -1)
{
return -1;
}
const int SCREEN_WIDTH = 640;
const int SCREEN_HEIGHT = 480;
const int SCREEN_BPP = 32;
SDL_Surface* screen = 0;
screen = SDL_SetVideoMode(
SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_BPP, SDL_SWSURFACE);
if(screen == NULL)
{
return 1;
}
SDL_WM_SetCaption("Hello world", NULL);
SmartImage smart_image;
smart_image.load("hello.bmp");
if(!smart_image.is_image_loaded())
{
return -2;
}
SmartImage second_smart_image(smart_image);
smart_image.clean_up();
apply_surface(0, 0, second_smart_image.content(), screen);
if(SDL_Flip(screen) == -1)
{
return -2;
}
SDL_Delay(3000);
second_smart_image.clean_up();
SDL_Quit();
std::cout << SDL_GetError() << std::endl;
return 0;
}

[/source]

I put here some of debug information. So if u don't need it. U can delete it.

I hope my solution will help you.


Here is my realization of your class.
  1. Why do you use a separate boolean variable to indicate if the surface member is NULL or not?
  2. You do not provide an assignment operator, thus violating the rule of three. You're going to be in for a bad time.
  3. Separate resource acquisition from construction is very 1970s C-retro and all, but what possible advantage does it convey in this scenario? It means you need to explicitly acquire and release resources separately from variable scope, which means each end every use of the object has to check to see if the object is valid. It means each and every possible scope exit needs to explicitly check if the object is valid and then release the resources, otherwise resources leak. It means more code and more opportunity for coding errors. It means harder to find bugs. It means your project will probably never ship.

Other than that, it looks fine.

Stephen M. Webb
Professional Free Software Developer


Here is my realization of your class.
  1. Why do you use a separate boolean variable to indicate if the surface member is NULL or not?
  2. You do not provide an assignment operator, thus violating the rule of three. You're going to be in for a bad time.
  3. Separate resource acquisition from construction is very 1970s C-retro and all, but what possible advantage does it convey in this scenario? It means you need to explicitly acquire and release resources separately from variable scope, which means each end every use of the object has to check to see if the object is valid. It means each and every possible scope exit needs to explicitly check if the object is valid and then release the resources, otherwise resources leak. It means more code and more opportunity for coding errors. It means harder to find bugs. It means your project will probably never ship.

Other than that, it looks fine.

All right!

You got me ;)

Here class with "rules of three":

[source]/*

* SmartImage.h
*
* Created on: Dec 7, 2013
* Author: Bohdan Kornienko
*/
#ifndef SMARTIMAGE_H_
#define SMARTIMAGE_H_
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
#include <string>
class SmartImage
{
private:
SDL_Surface *surface;
SDL_Surface* load_image(std::string file_name);
public:
SmartImage();
SmartImage(const SmartImage& param);
virtual ~SmartImage();
SmartImage& operator= (const SmartImage& rhd);
SDL_Surface* content() const;
bool load(std::string filename);
void copy_surface(SDL_Surface *target_surface);
bool is_image_loaded();
bool clean_up();
};
#endif /* SMARTIMAGE_H_ */

[/source]

[source]

/*
* SmartImage.cpp
*
* Created on: Dec 7, 2013
* Author: Bohdan Kornienko
*/
#include "SmartImage.h"
SmartImage::SmartImage()
: surface(NULL)
{
}
SmartImage::SmartImage(const SmartImage& param)
{
surface = SDL_DisplayFormat(param.content());
}
SmartImage::~SmartImage()
{
// TODO: put something here when is need
}
SDL_Surface* SmartImage::load_image(std::string file_name)
{
SDL_Surface* loadedImage = NULL;
SDL_Surface* optimizedImage = NULL;
loadedImage = IMG_Load(file_name.c_str());
optimizedImage = SDL_DisplayFormat(loadedImage);
SDL_FreeSurface(loadedImage);
return optimizedImage;
}
bool SmartImage::load(std::string filename)
{
if(surface == NULL)
{
surface = load_image(filename);
}
else
{
SDL_FreeSurface(surface);
surface = load_image(filename);
}
return (surface != NULL ? true : false);
}
void SmartImage::copy_surface(SDL_Surface *temp_surface)
{
if(surface != NULL)
{
SDL_FreeSurface(surface);
surface = NULL;
}
surface = SDL_DisplayFormat(temp_surface);
}
bool SmartImage::is_image_loaded()
{
return (surface != NULL ? true : false);
}
SDL_Surface* SmartImage::content() const
{
return surface;
}
SmartImage& SmartImage::operator =(const SmartImage& rhd)
{
if(this != &rhd)
{
SDL_FreeSurface(surface);
SDL_Surface* new_surface = NULL;
new_surface = SDL_DisplayFormat(rhd.surface);
surface = new_surface;
}
return *this;
}
bool SmartImage::clean_up()
{
SDL_FreeSurface(surface);
surface = NULL;
return true;
}

[/source]

This topic is closed to new replies.

Advertisement