Shared_ptr usage

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;
};


Then consider the 4 attempts I made
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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;
}


Thanks a lot for any help.
Last edited on
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.

1
2
Font(const Font&) = delete;
Font& operator=(const Font&) = delete;

This has the advantage that you will get a compilation error right away if you accidentally make a copy somewhere in the code.
Last edited on
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.
Last edited on
Sounds like what you want is for every Text object to have a shared_ptr to the same Font object.

Something like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#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 
Last edited on
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.
If you find yourself typing new or delete, something has gone wrong.
Topic archived. No new replies allowed.