code_junkie

A Question On Smart Pointers and Their Inevitable Indeterminism

Nov 13th, 2011
99
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 8.18 KB | None | 0 0
  1. class System {
  2. public:
  3. boost::shared_ptr< Subsystem > GetSubsystem( unsigned int index ) {
  4. assert( index < mSubsystems.size() );
  5. return mSubsystems[ index ];
  6. }
  7.  
  8. void LogMessage( const std::string& message ) {
  9. std::cout << message << std::endl;
  10. }
  11.  
  12. private:
  13. typedef std::vector< boost::shared_ptr< Subsystem > > SubsystemList;
  14. SubsystemList mSubsystems;
  15. };
  16.  
  17. class Subsystem {
  18. public:
  19. Subsystem( System* pParentSystem )
  20. : mpParentSystem( pParentSystem ) {
  21. }
  22.  
  23. ~Subsystem() {
  24. pParentSubsystem->LogMessage( "Destroying..." );
  25. // Destroy this subsystem: deallocate memory, release resource, etc.
  26. }
  27.  
  28. /*
  29. Other stuff here
  30. */
  31.  
  32. private:
  33. System * pParentSystem; // raw pointer to avoid cycles - can also use weak_ptrs
  34. };
  35.  
  36. int main() {
  37. {
  38. boost::shared_ptr< Subsystem > pSomeSubsystem;
  39. {
  40. boost::shared_ptr< System > pSystem( new System );
  41. pSomeSubsystem = pSystem->GetSubsystem( /* some index */ );
  42.  
  43. } // Our System would go out of scope and be destroyed here, but the Subsystem that pSomeSubsystem points to will not be destroyed.
  44.  
  45. } // pSomeSubsystem would go out of scope here but wait a second, how are we going to log messages in Subsystem's destructor?! Its parent System is destroyed after all. BOOM!
  46.  
  47. return 0;
  48. }
  49.  
  50. #include <iostream>
  51. #include <string>
  52. #include <vector>
  53.  
  54. #include <boost/shared_ptr.hpp>
  55.  
  56.  
  57. // Forward Declarations to allow friending
  58. class System;
  59. class ProxyableSubsystemBase;
  60.  
  61. // Base defining the interface for Subsystems
  62. class SubsystemBase
  63. {
  64. public:
  65. // pure virtual functions
  66. virtual void DoSomething(void) = 0;
  67. virtual int GetSize(void) = 0;
  68.  
  69. virtual ~SubsystemBase() {} // virtual destructor for base class
  70. };
  71.  
  72.  
  73. // Null Object Pattern: an object which implements the interface to do nothing.
  74. class NullSubsystem : public SubsystemBase
  75. {
  76. public:
  77. // implements pure virtual functions from SubsystemBase to do nothing.
  78. void DoSomething(void) { }
  79. int GetSize(void) { return -1; }
  80.  
  81. // Singleton Pattern: We only ever need one NullSubsystem, so we'll enforce that
  82. static NullSubsystem *instance()
  83. {
  84. static NullSubsystem singletonInstance;
  85. return &singletonInstance;
  86. }
  87.  
  88. private:
  89. NullSubsystem() {} // private constructor to inforce Singleton Pattern
  90. };
  91.  
  92.  
  93. // Proxy Pattern: An object that takes the place of another to provide better
  94. // control over the uses of that object
  95. class SubsystemProxy : public SubsystemBase
  96. {
  97. friend class ProxyableSubsystemBase;
  98.  
  99. public:
  100. SubsystemProxy(SubsystemBase *ProxiedSubsystem)
  101. : mProxied(ProxiedSubsystem)
  102. {
  103. }
  104.  
  105. // implements pure virtual functions from SubsystemBase to forward to mProxied
  106. void DoSomething(void) { mProxied->DoSomething(); }
  107. int GetSize(void) { return mProxied->GetSize(); }
  108.  
  109. protected:
  110. // State Pattern: the initial state of the SubsystemProxy is to point to a
  111. // valid SubsytemBase, which is passed into the constructor. Calling Nullify()
  112. // causes a change in the internal state to point to a NullSubsystem, which allows
  113. // the proxy to still perform correctly, despite the Subsystem going out of scope.
  114. void Nullify()
  115. {
  116. mProxied=NullSubsystem::instance();
  117. }
  118.  
  119. private:
  120. SubsystemBase *mProxied;
  121. };
  122.  
  123.  
  124. // A Base for real Subsystems to add the Proxying behavior
  125. class ProxyableSubsystemBase : public SubsystemBase
  126. {
  127. friend class System; // Allow system to call our GetProxy() method.
  128.  
  129. public:
  130. ProxyableSubsystemBase()
  131. : mProxy(new SubsystemProxy(this)) // create our proxy object
  132. {
  133. }
  134. ~ProxyableSubsystemBase()
  135. {
  136. mProxy->Nullify(); // inform our proxy object we are going away
  137. }
  138.  
  139. protected:
  140. boost::shared_ptr<SubsystemProxy> GetProxy() { return mProxy; }
  141.  
  142. private:
  143. boost::shared_ptr<SubsystemProxy> mProxy;
  144. };
  145.  
  146.  
  147. // the managing system
  148. class System
  149. {
  150. public:
  151. typedef boost::shared_ptr< SubsystemProxy > SubsystemHandle;
  152. typedef boost::shared_ptr< ProxyableSubsystemBase > SubsystemPtr;
  153.  
  154. SubsystemHandle GetSubsystem( unsigned int index )
  155. {
  156. assert( index < mSubsystems.size() );
  157. return mSubsystems[ index ]->GetProxy();
  158. }
  159.  
  160. void LogMessage( const std::string& message )
  161. {
  162. std::cout << " <System>: " << message << std::endl;
  163. }
  164.  
  165. int AddSubsystem( ProxyableSubsystemBase *pSubsystem )
  166. {
  167. LogMessage("Adding Subsystem:");
  168. mSubsystems.push_back(SubsystemPtr(pSubsystem));
  169. return mSubsystems.size()-1;
  170. }
  171.  
  172. System()
  173. {
  174. LogMessage("System is constructing.");
  175. }
  176.  
  177. ~System()
  178. {
  179. LogMessage("System is going out of scope.");
  180. }
  181.  
  182. private:
  183. // have to hold base pointers
  184. typedef std::vector< boost::shared_ptr<ProxyableSubsystemBase> > SubsystemList;
  185. SubsystemList mSubsystems;
  186. };
  187.  
  188. // the actual Subsystem
  189. class Subsystem : public ProxyableSubsystemBase
  190. {
  191. public:
  192. Subsystem( System* pParentSystem, const std::string ID )
  193. : mParentSystem( pParentSystem )
  194. , mID(ID)
  195. {
  196. mParentSystem->LogMessage( "Creating... "+mID );
  197. }
  198.  
  199. ~Subsystem()
  200. {
  201. mParentSystem->LogMessage( "Destroying... "+mID );
  202. }
  203.  
  204. // implements pure virtual functions from SubsystemBase
  205. void DoSomething(void) { mParentSystem->LogMessage( mID + " is DoingSomething (tm)."); }
  206. int GetSize(void) { return sizeof(Subsystem); }
  207.  
  208. private:
  209. System * mParentSystem; // raw pointer to avoid cycles - can also use weak_ptrs
  210. std::string mID;
  211. };
  212.  
  213.  
  214.  
  215. //////////////////////////////////////////////////////////////////
  216. // Actual Use Example
  217. int main(int argc, char* argv[])
  218. {
  219.  
  220. std::cout << "main(): Creating Handles H1 and H2 for Subsystems. " << std::endl;
  221. System::SubsystemHandle H1;
  222. System::SubsystemHandle H2;
  223.  
  224. std::cout << "-------------------------------------------" << std::endl;
  225. {
  226. std::cout << " main(): Begin scope for System." << std::endl;
  227. System mySystem;
  228. int FrankIndex = mySystem.AddSubsystem(new Subsystem(&mySystem, "Frank"));
  229. int ErnestIndex = mySystem.AddSubsystem(new Subsystem(&mySystem, "Ernest"));
  230.  
  231. std::cout << " main(): Assigning Subsystems to H1 and H2." << std::endl;
  232. H1=mySystem.GetSubsystem(FrankIndex);
  233. H2=mySystem.GetSubsystem(ErnestIndex);
  234.  
  235.  
  236. std::cout << " main(): Doing something on H1 and H2." << std::endl;
  237. H1->DoSomething();
  238. H2->DoSomething();
  239. std::cout << " main(): Leaving scope for System." << std::endl;
  240. }
  241. std::cout << "-------------------------------------------" << std::endl;
  242. std::cout << "main(): Doing something on H1 and H2. (outside System Scope.) " << std::endl;
  243. H1->DoSomething();
  244. H2->DoSomething();
  245. std::cout << "main(): No errors from using handles to out of scope Subsystems because of Proxy to Null Object." << std::endl;
  246.  
  247. return 0;
  248. }
  249.  
  250. main(): Creating Handles H1 and H2 for Subsystems.
  251. -------------------------------------------
  252. main(): Begin scope for System.
  253. <System>: System is constructing.
  254. <System>: Creating... Frank
  255. <System>: Adding Subsystem:
  256. <System>: Creating... Ernest
  257. <System>: Adding Subsystem:
  258. main(): Assigning Subsystems to H1 and H2.
  259. main(): Doing something on H1 and H2.
  260. <System>: Frank is DoingSomething (tm).
  261. <System>: Ernest is DoingSomething (tm).
  262. main(): Leaving scope for System.
  263. <System>: System is going out of scope.
  264. <System>: Destroying... Frank
  265. <System>: Destroying... Ernest
  266. -------------------------------------------
  267. main(): Doing something on H1 and H2. (outside System Scope.)
  268. main(): No errors from using handles to out of scope Subsystems because of Proxy to Null Object.
  269.  
  270. class Subsystem
  271. {
  272. ...
  273. ~Subsystem() {
  274. boost::shared_ptr<System> my_system(pParentSystem.lock());
  275.  
  276. if (NULL != my_system.get()) { // only works if pParentSystem refers to a valid System object
  277. // now you are guaranteed this will work, since a reference is held to the System object
  278. my_system->LogMessage( "Destroying..." );
  279. }
  280. // Destroy this subsystem: deallocate memory, release resource, etc.
  281.  
  282. // when my_system goes out of scope, this may cause the associated System object to be destroyed as well (if it holds the last reference)
  283. }
  284. ...
  285. };
Add Comment
Please, Sign In to add comment