fork download
  1. /*
  2.   This is an evolved solution based on an SO answer found
  3.   at http://stackoverflow.com/a/17159495/845568
  4.  
  5.   1. I changed the name from `Data` to `Value` as it seems more logical in
  6.   this case.
  7.   2. It uses the same PIMPL approach as the original.
  8.   3. It uses `std::unqiue_ptr` instead of `new` and `delete` to manage object
  9.   lifetime
  10.   4. It uses an interface approach for the base class of the value container.
  11.   An implementation in the base class is unnecessary.
  12.   5. It uses a template class for the value container and includes a
  13.   specialization for `void`. This allows for a default constructor in `Value`.
  14. */
  15. #include <iostream>
  16. #include <memory>
  17.  
  18. // Interface for value objects
  19. struct ValueImpl
  20. {
  21. virtual ~ValueImpl() {};
  22. virtual std::unique_ptr<ValueImpl> clone() const = 0;
  23. virtual void increments() = 0;
  24. virtual void print() const = 0;
  25. };
  26.  
  27. // Templated implementation of value class for easier use
  28. template<typename T>
  29. class ValueT : public ValueImpl
  30. {
  31. public:
  32.  
  33. ValueT(const T &k) : value_(k) {}
  34.  
  35. virtual std::unique_ptr<ValueImpl> clone() const
  36. {
  37. return std::unique_ptr<ValueImpl>(new ValueT(value_));
  38. }
  39.  
  40. void increments()
  41. {
  42. value_++;
  43. }
  44.  
  45. void print() const
  46. {
  47. std::cout << typeid(T).name() << "(" << value_ << ") " << std::endl;
  48. }
  49.  
  50. private:
  51.  
  52. T value_;
  53. };
  54.  
  55. // Specialization of value class to provide a default ctor in Value
  56. template<>
  57. class ValueT<void> : public ValueImpl
  58. {
  59. public:
  60.  
  61. ValueT() {}
  62.  
  63. virtual std::unique_ptr<ValueImpl> clone() const
  64. {
  65. return std::unique_ptr<ValueImpl>(new ValueT());
  66. }
  67.  
  68. void increments() { }
  69.  
  70. void print() const
  71. {
  72. std::cout << "(void)" << std::endl;
  73. }
  74. };
  75.  
  76.  
  77. // Value container using PIMPL idiom.
  78. class Value
  79. {
  80. public:
  81.  
  82. Value() : value_(new ValueT<void>) {}
  83. Value(const Value & other) : value_(other.value_->clone()) {}
  84.  
  85. // Construct a Value given an actual implementation:
  86. // This allocates memory on the heap, hidden in clone()
  87. explicit Value(const ValueImpl& value) : value_(value.clone()) {}
  88.  
  89. // Destruct the data: must deallocate the memory
  90. virtual ~Value() {}
  91.  
  92. // Copy constructor and assignment operator:
  93. // Implements a value semantics by allocating new memory
  94. Value & operator=(const Value & other)
  95. {
  96. if(&other != this)
  97. {
  98. value_ = std::move(other.value_->clone());
  99. }
  100. return *this;
  101. }
  102.  
  103. // Custom "polymorphic" methods
  104. void increments() { value_->increments(); }
  105. void print() { value_->print(); }
  106.  
  107. private:
  108.  
  109. std::unique_ptr<ValueImpl> value_;
  110. };
  111.  
  112.  
  113.  
  114. int main()
  115. {
  116. // use typedefs if you like. they are really not necessary for this example
  117. Value iv(ValueT<int>(1));
  118. Value dv(ValueT<double>(1.2));
  119.  
  120. Value value;
  121.  
  122. value.print();
  123. value = dv;
  124. value.print();
  125. value = iv;
  126. value.print();
  127. value.increments();
  128. value.print();
  129. }
  130.  
Success #stdin #stdout 0s 2988KB
stdin
Standard input is empty
stdout
(void)
d(1.2) 
i(1) 
i(2)