|
|
|
|
||||||
![]() |
|
|
LinkBack | Outils de la discussion |
|
|
#1 (permalink) |
|
Messages: n/a
Hébergeur: |
Hello!
I have just discovered a way to use the private implementation idiom (pimpl), without the overhead of dynamic memory allocation. For those of you who don't know what this is, Wikipedia has a nice article you can read. Anyway, I discovered that if you make all members in the implementation class mutable, you can in fact use this idiom without any "unnecessary" memory allocation. Here's a minimal example of the method: // In the header of your class called Line #include <string> class Line { public: Line(const std::string& name); const std::string& GetName() const; void SetName(const std::string& s); private: // Private implementation idiom: // all member variables are hidden in this class class LineImpl; const LineImpl& m_pimpl; // normally a non-const pointer }; // and in your implementation file: #include "Line.h" // Here we define the class with the member variables class Line::LineImpl { public: LineImpl(const std::string& s) : m_s(s) {} // all methods need to be const here const std::string& GetName() const { return m_s; } void SetName(const std::string& s) const { m_s = s; } private: mutable std::string m_s; // the trick! all members are mutable }; // create the pimpl instance without using new Line::Line(const std::string& s) : m_pimpl(LineImpl(s)) {} // forward all member functions to the private implementation const std::string& Line::GetName() const { return m_pimpl.GetName(); } void Line::SetName(const std::string& s) { m_pimpl.SetName(s); } Ok experts, what do you all think? This method sacrifies const-correctness for some extra speed. Is it worth it? -- Daniel |
|
|
|
#2 (permalink) |
|
Messages: n/a
Hébergeur: |
Daniel Lidström a écrit :
> Hello! > > I have just discovered a way to use the private implementation idiom > (pimpl), without the overhead of dynamic memory allocation. For those of > you who don't know what this is, Wikipedia has a nice article you can > read. Anyway, I discovered that if you make all members in the > implementation class mutable, you can in fact use this idiom without any > "unnecessary" memory allocation. Here's a minimal example of the method: > > // In the header of your class called Line > > #include <string> > > class Line > { > public: > > Line(const std::string& name); > const std::string& GetName() const; > void SetName(const std::string& s); > > private: > > // Private implementation idiom: > // all member variables are hidden in this class > class LineImpl; > const LineImpl& m_pimpl; // normally a non-const pointer > }; > > // and in your implementation file: > > #include "Line.h" > > // Here we define the class with the member variables [snip] > // all methods need to be const here [snip] > mutable std::string m_s; // the trick! all members are mutable Which mean you coerce the code into compilation. That's all. > // create the pimpl instance without using new > Line::Line(const std::string& s) : m_pimpl(LineImpl(s)) {} Your local is destroyed when going out of scope. Doesn't it ? > [snip] > Ok experts, what do you all think? This method sacrifies > const-correctness for some extra speed. Is it worth it? Not really. And certainly not worth a dangling reference. Michael |
|
|
|
#3 (permalink) |
|
Messages: n/a
Hébergeur: |
In article <4716585e$0$25087$426a74cc@news.free.fr>,
Michael DOUBEZ <michael.doubez@free.fr> wrote: > Daniel Lidström a écrit : > > // create the pimpl instance without using new > > Line::Line(const std::string& s) : m_pimpl(LineImpl(s)) {} > > Your local is destroyed when going out of scope. Doesn't it ? No it isn't. It is actually ok to bind a temporary object to a const reference. There will be no "dangling" reference. -- Daniel |
|
|
|
#4 (permalink) |
|
Messages: n/a
Hébergeur: |
Daniel Lidström a écrit :
> In article <4716585e$0$25087$426a74cc@news.free.fr>, > Michael DOUBEZ <michael.doubez@free.fr> wrote: > >> Daniel Lidström a écrit : >>> // create the pimpl instance without using new >>> Line::Line(const std::string& s) : m_pimpl(LineImpl(s)) {} >> Your local is destroyed when going out of scope. Doesn't it ? > > No it isn't. It is actually ok to bind a temporary object to a const > reference. There will be no "dangling" reference. It is ok to bind it but that doesn't mean the lifetime of the temporary is extended. Example: const std::string& foo() { return std::string("bar"); } The value returned by foo() is an dangling reference. Michael |
|
|
|
#5 (permalink) |
|
Messages: n/a
Hébergeur: |
Daniel Lidström <somebody@microsoft.com> wrote:
> // In the header of your class called Line > > #include <string> > > class Line > { > public: > > Line(const std::string& name); > const std::string& GetName() const; > void SetName(const std::string& s); > > private: > > // Private implementation idiom: > // all member variables are hidden in this class > class LineImpl; > const LineImpl& m_pimpl; // normally a non-const pointer > }; > > // and in your implementation file: > > #include "Line.h" > > // Here we define the class with the member variables > class Line::LineImpl > { > public: > > LineImpl(const std::string& s) : m_s(s) {} > // all methods need to be const here > const std::string& GetName() const { return m_s; } > void SetName(const std::string& s) const { m_s = s; } > > private: > > mutable std::string m_s; // the trick! all members are mutable > }; > > // create the pimpl instance without using new > Line::Line(const std::string& s) : m_pimpl(LineImpl(s)) {} Where would the memory for the LineImpl object be placed? It isn't embedded in the object, nor is it in the heap, and it can't be placed on the stack (and still survive the call to the c_tor.) Doesn't sound like a good idea to me. |
|
|
|
#6 (permalink) |
|
Messages: n/a
Hébergeur: |
Daniel Lidström wrote:
> I have just discovered a way to use the private implementation idiom > (pimpl), without the overhead of dynamic memory allocation. [ storing a reference to a temporary ] As you noticed, this doesn't work. However, there is a method that works. All you have to do is to add a suitably aligned and sufficiently large buffer into the class: class foo { aligned_storage<42> m_impl; class implementation; foo(); ~foo(); void some_function(); }; class foo::implementation { ... }; foo::foo() { // placement new new m_impl.get<void>() implementation; } foo::~foo() { // explicit dtor invokation m_impl.get<implementation>()->~implementation; } void foo::some_function() { m_impl.get<implementation>()->some_function(); } Is it worth the hassle? Typically not, in particular since it's hard to guarantee that you have both enough but still not too much memory. Uli |
|
|
|
#7 (permalink) |
|
Messages: n/a
Hébergeur: |
On Oct 17, 10:36 pm, "Daniel T." <danie...@earthlink.net> wrote:
> Daniel Lidström <someb...@microsoft.com> wrote: > > // In the header of your class called Line > > #include <string> > > class Line > > { > > public: > > Line(const std::string& name); > > const std::string& GetName() const; > > void SetName(const std::string& s); > > private: > > // Private implementation idiom: > > // all member variables are hidden in this class > > class LineImpl; > > const LineImpl& m_pimpl; // normally a non-const pointer > > }; > > // and in your implementation file: > > #include "Line.h" > > // Here we define the class with the member variables > > class Line::LineImpl > > { > > public: > > LineImpl(const std::string& s) : m_s(s) {} > > // all methods need to be const here > > const std::string& GetName() const { return m_s; } > > void SetName(const std::string& s) const { m_s = s; } > > private: > > mutable std::string m_s; // the trick! all members are mutable > > }; > > // create the pimpl instance without using new > > Line::Line(const std::string& s) : m_pimpl(LineImpl(s)) {} > Where would the memory for the LineImpl object be placed? It > isn't embedded in the object, nor is it in the heap, and it > can't be placed on the stack (and still survive the call to > the c_tor.) >From the standard (§12.2/5): "A temporary bound to a reference member in a constructor's ctor-initializer persists until the constructor exits." In colloquial terms: the temporary is created on the stack, and destructed before returning from the constructor. > Doesn't sound like a good idea to me. It isn't, unless you like undefined behavior and hard to find bugs. -- James Kanze (GABI Software) email:james.kanze@gmail.com Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34 |
|
|
|
#8 (permalink) |
|
Messages: n/a
Hébergeur: |
In article <47165e09$0$26728$426a74cc@news.free.fr>,
Michael DOUBEZ <michael.doubez@free.fr> wrote: >Daniel Lidström a écrit : >> In article <4716585e$0$25087$426a74cc@news.free.fr>, >> Michael DOUBEZ <michael.doubez@free.fr> wrote: >> >>> Daniel Lidström a écrit : >>>> // create the pimpl instance without using new >>>> Line::Line(const std::string& s) : m_pimpl(LineImpl(s)) {} >>> Your local is destroyed when going out of scope. Doesn't it ? >> >> No it isn't. It is actually ok to bind a temporary object to a const >> reference. There will be no "dangling" reference. > >It is ok to bind it but that doesn't mean the lifetime of the temporary >is extended. > >Example: >const std::string& foo() >{ > return std::string("bar"); >} > >The value returned by foo() is an dangling reference. > Euh, it's not what he is doing, it's more like: std::string foo() { return std::string("bar"); } int main() { std::string const & val = foo(); .... |
|
![]() |
| Outils de la discussion | |
|
|