I want to use std::shared_ptr to keep a pointer to a big object and I can't figure out how to initialize this pointer.
I have a class Font which contains the bitmap font (roughly 8MB).
Then I have a class Text which needs to keep a pointer to a font object to render the text. I wish to use smart pointers to ensure that I never access an empty pointer (that is: if the Font object goes out of scope, the smart pointer should avoid the object to be destroyed).
Here is the minimal code to explain my problem
1 2 3 4 5 6 7 8 9 10 11
class Font {
public:
int i; //in the real scenario this will be an array of 8 MB
Font() :i(0){};
Font(const Font & f) : i(f.i){puts("font copied! DON'T");};
};
class Text{
public:
std::shared_ptr<Font> ptr;
};
int main() {
//case 1
Font f;
f.i = 10;
Text text1;
text1.ptr = std::shared_ptr<Font>(&f); //error: pointer being freed was not allocated;
// I believe the object f is destroyed, the text1 tries to free the shared pointer
//case 2
Text text2;
text2.ptr = std::make_shared<Font>(f); //font copied! DON'T
Text text3;
text3.ptr = std::make_shared<Font>(f); //font copied! DON'T
printf("%li \n", text3.ptr.use_count()); // 1; object is not shared
//case 3
Font * f2 = new Font;
f2->i = 3;
Text text4;
text4.ptr = std::shared_ptr<Font>(f2);
//I believe the shared pointer cares about the pointer and not the object
delete f2;
f2 = new Font;
f2->i = 4;
printf("%i \n", text4.ptr->i); // 4
//case 4
Text text5;
text5.ptr = std::make_shared<Font>(*f2); //font copied! DON'T
Text text6;
text6.ptr = std::make_shared<Font>(*f2); //font copied! DON'T
printf("%li \n", text6.ptr.use_count()); // 1
return 0;
}
std::make_shared<Font> constructs a new Font object. The arguments that you pass will be forwarded to the Font constructor. If you want to use the default constructor you don't pass any arguments.
text.ptr = std::shared_ptr<Font>();
Font(const Font & f) : i(f.i){puts("font copied! DON'T");};
If don't want it to be possible to copy the Font objects you can mark the copy constructor and copy assignment operator as deleted.
Thanks for the reply Peter87. However, this does not quite solve my problem.
The point is that I don't want to create new Font objects and as you said I could mark the copy assignment and copy constructor as deleted.
Done that, how do I create a smart pointer to a Font object?
If I create the smart pointer using a pointer to the Font object (as done in case 1 and 3) I still don't get the behavior I want.
In case 1 I get a run-time error with the shared pointer trying to free a non-allocated pointer.
In case 3 the shared pointer follows the pointer and not the object, so code like this will create issues:
1 2 3 4 5 6 7 8 9 10
Font * font;
font->load(Arial); //this is pseudo-code
Text text1;
text1.ptr = std::shared_ptr<Font>(font); //set font to Arial
delete font;
font->load(Times New Roman);
Text text2;
text2.ptr = std::shared_ptr<Font>(font); //set font to Times New Roman
text1.render("hello"); //this is rendered using times new roman and not arial!!
text2.render("hello");
The point is: I want multiple text object that use the same font, to share the Font object without duplicating it. Moreover, I want the Text class to be independent of how the Font class is handled.
#include <iostream>
#include <string>
#include <memory>
using std::shared_ptr;
using std::string;
using std::cout;
class Font
{
public:
std::string currentFont;
void load(std::string input) { currentFont = input; }
};
class Text
{
public:
Text(shared_ptr<Font> pFont) { m_pFont = pFont; }
void render(std::string input) { cout << input << " " << m_pFont->currentFont << '\n'; }
shared_ptr<Font> m_pFont;
};
int main()
{
// Create ONE Font object, held by a shared_ptr
shared_ptr<Font> pTheOneAndOnlyFontObject(std::make_shared<Font>());
// Give that shared_ptr to each Text object; they copy the shared_ptr,
// so both Text objects have a pointer to the SAME Font object
Text text1(pTheOneAndOnlyFontObject);
Text text2(pTheOneAndOnlyFontObject);
text1.m_pFont->load("Times New Roman"); //set font to Times New Roman
text1.render("hello from text 1"); // renders in Times New Roman
text2.m_pFont->load("Arial"); //set font to Arial
text2.render("hello from text 2"); // renders in Arial
text1.render("hello from text 1"); // renders in Arial
}
Once you've got that, you can get slightly fancy. Maybe you'd like it to work like this: Text text3 = text2; // Now, the newly created text3 is using the same Font object as text2
You can do that.
Or maybe you'd like something like this: Text commonHeadingText(fontManager->getCommonHeadingFont());
Here, there is a "fontManager" which holds a shared pointer to various fonts that are used for different things - plain text, headings, bold text and so on - and a Text object can simply ask for the appropriate Font. You can do this.
Or, you could have something like this:
1 2
Text text1; // THIS Text object creates a brand new Font object
Text text2(text1); // THIS text object does NOT create a new one, but shares the Font object with text1
Thanks for your reply Repeater. I believe that a FontManager is what I was looking for. So I can keep a list of all active fonts and the Text object can get the required font from such list.
Going back to shared pointers. It seems I need to initialize the Font object as a shared_ptr and create all other shared_ptr's using the first one.