Smart Pointer Inside a Class/Struct

I am reasonably familiar with the concept of smart pointers. But I struggle to get my head around how these would be used as members of a class/struct.

Question One:

Just for examples sake I have created a class which contains the member name and size of the char array holding the name:

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
class A
{
	unsigned int m_Size; char* m_Name;
public:
	A(const char* name)
	{
		m_Size = strlen(name)+1;
		m_Name = new char[m_Size];
		memcpy(m_Name, name, m_Size);
		std::cout << "Member name: " << m_Name << std::endl;
	}

	~A()
	{
		delete[] m_Name;
	}
};

int main()
{
	
	{
		A a1("Jolene\0"); 
	} 

}

This program works as I would expect, but 'new' and 'delete' are explicitly called. I would prefer to use a smart pointer (if possible) instead, so that the memory management is automated to a certain extent:

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
class B
{
	unsigned int m_Size; std::shared_ptr<char[]> m_Name;
public:
	B(const char* name)
	{
		m_Size = strlen(name) + 1;
		m_Name = std::make_shared<char[m_Size]>();
		memcpy(m_Name, name, m_Size);
		std::cout << "Member name: " << m_Name << std::endl;
	}

	~B()
	{
		std::cout << "Member name: " << m_Name << std::endl;
	}
};

int main()
{

	{
		B b1("Jolene\0");
	}

}


On the line:

m_Name = std::make_shared<char[m_Size]>();

I get two errors:

1). 'non-constant expression as array bound'
2). 'binary '='. no operator found which takes a right-hand operand of type 'overloaded-function' (or there is no acceptable conversion)'

And on the line:

memcpy(m_Name, name, m_Size);

I get a further two errors:

1). 'no suitable conversion function from "std::shared_ptr<char[]>" to "void *" exists'
2). ' 'void * memcpy(void *,const void *,size_t)': cannot convert argument 1 from 'std::shared_ptr<char[]>' to 'void *'

Is it possible for this code to be corrected so that 'm_Name' is constructed as a char pointer via a smart pointer rather than by explicitly calling 'new' and 'delete' as in my first example?

Question Two:

In the end I managed to find a similar way of doing this that worked, but I had to use std::string rather than a char pointer in this example:

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
class C
{
	std::unique_ptr<std::string> m_Name;
public:
	C(std::string name)
		: m_Name(std::make_unique<std::string>())
	{
		*m_Name = name;
		std::cout << "Member name: " << *m_Name << std::endl;
	}

	~C()
	{
		std::cout << "Member name: " << *m_Name << std::endl;
	}
};

int main()
{

	{
		C c1("Jolene\0");
	}

}

'm_Name' is initialised successfully via the constructor, but in the destructor, I am still able to output 'm_Name', which surely means that it hasn't been automatically deleted by the smart pointer?

Am I right in thinking that 'm_Name' should be deleted automatically by the smart pointer and if so, where does this deletion take place?
Last edited on
Anything you write in the destructor happens BEFORE anything that you get for free. So the destruction of member variables happens AFTER your code in the destuctor. The smart pointer doesn't go through its destruction until AFTER your destructor code has run.
This might be closer to your original aims:

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
#include <memory>
#include <cstring>
#include <iostream>

class B
{
	unsigned int m_Size; std::unique_ptr<char[]> m_Name;
public:
	B(const char* name)
	{
		m_Size = strlen(name) + 1;
		m_Name = std::make_unique<char[]>( m_Size );
		memcpy(m_Name.get(), name, m_Size);
		std::cout << "Member name: " << m_Name.get() << std::endl;
	}

	~B()
	{
		std::cout << "Member name: " << m_Name.get() << std::endl;
	}
};

int main()
{
	{
		B b1("Jolene\0");
	}
}
Repeater said
The smart pointer doesn't go through its destruction until AFTER your destructor code has run.

Okay that makes sense. Thanks for clearing that up.

Last edited on
I get two errors:

1). 'non-constant expression as array bound'

Array sizes must be compile time constants in C++.
2). 'binary '='. no operator found which takes a right-hand operand of type 'overloaded-function' (or there is no acceptable conversion)'

You're not calling the function correctly, try (where m_Size is a compile time constant):
m_Name = std::make_shared<char[]>(m_Size);

I get a further two errors:

1). 'no suitable conversion function from "std::shared_ptr<char[]>" to "void *" exists'
2). ' 'void * memcpy(void *,const void *,size_t)': cannot convert argument 1 from 'std::shared_ptr<char[]>' to 'void *'

Why are you using memcpy instead of strcpy()?


In the end I managed to find a similar way of doing this that worked, but I had to use std::string rather than a char pointer in this example:

But why are you using a pointer to a std::string instead of just using a normal, non-pointer instance?

I am still able to output 'm_Name', which surely means that it hasn't been automatically deleted by the smart pointer?

How are you trying to output m_Name? When?

Should 'm_Name' be deleted automatically by the smart pointer and if so, where does this deletion take place?

When the class instance goes out of scope.



Repeater said
This might be closer to your original aims:

Yes, that's exactly what I was aiming for. Thanks again.
Last edited on
jlb:

In the second question, the example I gave was probably quite poor, as I mentioned at the start, the examples were just created for the purpose of addressing the question of how smart pointers operate when used on a class/struct member, although I appreciate this particular example was impractical so apologies for that.

How are you trying to output m_Name? When?

In the destructor on the third code excerpt from my original post:

1
2
3
4
	~C()
	{
		std::cout << "Member name: " << *m_Name << std::endl;
	}


When the class instance goes out of scope.

Yes I understand this, I just thought that the deletion occurs before the destructor is called, but as Repeater mentioned, deletion actually happens after the destructor has run.

Thanks for your input.
Last edited on
I am reasonably familiar with the concept of smart pointers.

Well, I’m aware I’m not.

Why do you want to use a smart pointer to access a raw pointer? What’s your purpose?
Smart pointers are there to replace raw pointers, where it’s possible.

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
#include <cstring>
#include <iostream>
#include <memory>


class MyClass {
public:
    MyClass(const char* name)
    {
        m_Size = strlen(name) + 1;
        m_Name = std::make_shared<char>(m_Size);
        std::strcpy(m_Name.get(), name);
        std::cout << "Member name: " << m_Name.get() << '\n';
    }

    ~MyClass()
    {
        std::cout << "Member name: " << m_Name.get() << '\n';
    }

private:
    std::size_t m_Size;
    std::shared_ptr<char> m_Name;
};


int main()
{
        MyClass("Jolene\0");
}



- - -
Please note: dramatically editing your posts could make the thread incomprehensible to future readers.
Well, I’m aware I’m not.

You're aware that you're not familiar with smart pointers?

Did you mean to say "I'm aware you're not"? If so then just to clarify, I am familiar with smart pointers when used outside of a class/struct, but I was confused over how they operate when used on a member of a class/struct.

Why do you want to use a smart pointer to access a raw pointer? What’s your purpose?

A lot of people seem to think that raw pointers shouldn't be used in 'modern C++' since the advent of smart pointers, so I just wanted to see how a smart pointer could replace explicit 'new' and 'delete' calls when used inside a class/struct, as I previously mentioned this was the concept I was struggling with - this was the purpose of the example.

Please note: dramatically editing your posts could make the thread incomprehensible to future readers.

Yes but I feel that my edits in this case actually made the thread more comprehensible, which is why I made them.

Thanks for your input Enoizat
Last edited on
You're aware that you're not familiar with smart pointers?

Yes, I am.

Did you mean to say "I'm aware you're not"?

No, I meant what I wrote.

A lot of people seem to think that raw pointers shouldn't be used in 'modern C++'

Really? What I’ve read so far is that they shouldn’t be used as much as before, but as far as I know the smart pointers purpose should be managing ownership, not standing in for raw pointers everywhere. I might be wrong.

Anyway, the main question was: since you want to use smart pointers in place of raw pointers, why do you use a smart pointer to access a raw pointer?
No, I meant what I wrote.

Okay my apologies.

Really? What I’ve read so far is that they shouldn’t be used as much as before, but as far as I know the smart pointers purpose should be managing ownership, not standing in for raw pointers everywhere. I might be wrong.

I think it depends on who you ask - everyone will have their own opinion, but I have heard that said and I think it makes sense. If you can use a smart pointer instead of a raw pointer, then why not, since smart pointers handle the memory management automatically.

Anyway, the main question was: since you want to use smart pointers in place of raw pointers, why do you use a smart pointer to access a raw pointer?

I don't want to use a smart pointer to access a raw pointer. I want to use a smart pointer instead of having to explicitly call 'new' and 'delete'.

I want to use a smart pointer instead of having to explicitly call 'new' and 'delete'.

Reading here:
https://stackoverflow.com/questions/40915580/using-smart-pointers-for-array
it seems if you want to create a smart pointer to a C-Style array
stackoverflow wrote:
you will need to provide a custom deleter

I have no idea if things have changed in the last year (that post is dated 17 Dicember 2016), and my knowledge about smart pointers is inaccurate.


- - -
P.s. the same thing here:
https://www.perforce.com/resources/qac/high-integrity-cpp-coding-standard
at point “17.3.4 Do not create smart pointers of array type”.
I have no idea if things have changed in the last year (that post is dated 17 Dicember 2016)

Things have been changing little by little:
std::unique_ptr supports arrays since C++11,
std::make_unique supports arrays since C++14,
std::shared_ptr supports arrays since C++17,
std::make_shared supports arrays since C++20
Thank you, Cubbi! I wouldn't have found those information.

Topic archived. No new replies allowed.