Main.cpp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351
  1. #include <cassert>
  2. #include <iostream>
  3. #include <vector>
  4. // this struct is used in the automated tests at the end of the file
  5. // it counts all instances with different numbers to ensure each object is
  6. // constructed and destructed the same amount
  7. struct A {
  8. static int instances;
  9. int a;
  10. A(int a) : a(a) {
  11. // std::cout << "construct A " << a << "\n";
  12. instances += a;
  13. }
  14. A(const A& other) : a(other.a) {
  15. // std::cout << "copy construct A " << a << "\n";
  16. instances += a;
  17. }
  18. A(A&& other) : a(other.a) {
  19. // std::cout << "move construct A " << a << "\n";
  20. instances += a;
  21. }
  22. A& operator=(const A& other) {
  23. instances -= a;
  24. a = other.a;
  25. instances += a;
  26. // std::cout << "copy assignment A " << a << "\n";
  27. return *this;
  28. }
  29. A& operator=(A&& other) {
  30. instances -= a;
  31. a = other.a;
  32. instances += a;
  33. // std::cout << "move assignment A " << a << "\n";
  34. return *this;
  35. }
  36. ~A() {
  37. // std::cout << "destruct A " << a << "\n";
  38. instances -= a;
  39. }
  40. };
  41. int A::instances = 0;
  42. template<typename T>
  43. class Vector {
  44. T* data;
  45. int capacity;
  46. int elements;
  47. public:
  48. Vector() : data(nullptr), capacity(0), elements(0) {
  49. }
  50. ~Vector() {
  51. // placement new needs explicit destructor calling
  52. for(int i = 0; i < elements; i++) {
  53. data[i].~T();
  54. }
  55. // casting this pointer not back to its origin type yields a crash
  56. delete[] reinterpret_cast<char*>(data);
  57. }
  58. // allocate new storage for copies
  59. Vector(const Vector& other)
  60. : data(allocate(other.capacity)), capacity(other.capacity),
  61. elements(other.elements) {
  62. // copy data into new storage
  63. for(int i = 0; i < elements; i++) {
  64. new(data + i) T(other.data[i]);
  65. }
  66. }
  67. // this handles copy and move assigment
  68. // copy: replace current resources with other made by the copy constructor
  69. // move: replace current resources with other made by the move constructor
  70. // other gets the old data and is destroyed by the destructor after going
  71. // out of scope
  72. Vector& operator=(Vector other) {
  73. swap(*this, other);
  74. return *this;
  75. }
  76. // construct a default vector so the other vector gets valid values from the
  77. // swap
  78. Vector(Vector&& other) : Vector() {
  79. swap(*this, other);
  80. }
  81. void reserve(int size) {
  82. if(size <= capacity) {
  83. return;
  84. }
  85. Vector v;
  86. v.capacity = size;
  87. v.data = allocate(size);
  88. for(int i = 0; i < elements; i++) {
  89. v.push_back(std::move(data[i]));
  90. }
  91. swap(*this, v);
  92. }
  93. void resize(int size, const T& t) {
  94. // elements will be equal to size after this call but not the capacity
  95. if(size > elements) {
  96. // fill until the given size is reached
  97. for(int i = elements; i < size; i++) {
  98. push_back(t);
  99. }
  100. } else if(size < elements) {
  101. // remove objects until the size matches
  102. for(int i = size; i < elements; i++) {
  103. data[i].~T();
  104. }
  105. elements = size;
  106. }
  107. }
  108. void resize(int size) {
  109. resize(size, T());
  110. }
  111. void push_back(const T& t) {
  112. ensureCapacity();
  113. new(data + elements++) T(t);
  114. }
  115. void push_back(T&& t) {
  116. ensureCapacity();
  117. // && would be lost without std::move
  118. new(data + elements++) T(std::move(t));
  119. }
  120. T& operator[](int index) {
  121. return data[index];
  122. }
  123. const T& operator[](int index) const {
  124. return data[index];
  125. }
  126. T& at(int index) {
  127. // std states "at" is [] with range check
  128. assert(index >= 0 && index < elements);
  129. return (*this)[index];
  130. }
  131. const T& at(int index) const {
  132. // std states "at" is [] with range check
  133. assert(index >= 0 && index < elements);
  134. return (*this)[index];
  135. }
  136. int size() const {
  137. return elements;
  138. }
  139. T* begin() {
  140. return data;
  141. }
  142. T* end() {
  143. return data + elements;
  144. }
  145. const T* begin() const {
  146. return data;
  147. }
  148. const T* end() const {
  149. return data + elements;
  150. }
  151. void erase(T* from, T* to) {
  152. T* remove = from;
  153. T* move = to;
  154. T* stop = end();
  155. // fill the hole by moving following objects as long as possible
  156. while(move != stop) {
  157. *remove = std::move(*move);
  158. remove++;
  159. move++;
  160. }
  161. // remove left over objects
  162. while(remove != stop) {
  163. remove->~T();
  164. remove++;
  165. elements--;
  166. }
  167. }
  168. void erase(T* start) {
  169. erase(start, start + 1);
  170. }
  171. T* as_array() {
  172. return begin();
  173. }
  174. const T* as_array() const {
  175. return begin();
  176. }
  177. void erase_by_swap(int index) {
  178. elements--;
  179. if(index != elements) {
  180. data[index] = std::move(data[elements]);
  181. }
  182. data[elements].~T();
  183. }
  184. private:
  185. static T* allocate(int length) {
  186. return reinterpret_cast<T*>(new char[sizeof(T) * length]);
  187. }
  188. static void swap(Vector& a, Vector& b) {
  189. std::swap(a.data, b.data);
  190. std::swap(a.capacity, b.capacity);
  191. std::swap(a.elements, b.elements);
  192. }
  193. void ensureCapacity() {
  194. if(elements >= capacity) {
  195. // doubling the size amortizes costs
  196. reserve(capacity == 0 ? 1 : capacity * 2);
  197. }
  198. }
  199. };
  200. void printError(int number) {
  201. std::cout << "\033[0;31mError " << number << "\033[0m\n";
  202. }
  203. // V is either std::vector or Vector to test for complete same behaviour in both
  204. // implementations
  205. template<typename V>
  206. void test() {
  207. {
  208. const int elements = 2;
  209. V v;
  210. for(int i = 0; i < elements; i++) {
  211. v.push_back(A(i));
  212. }
  213. const V& cv = v;
  214. for(int i = 0; i < elements; i++) {
  215. if(v[i].a != i || cv[i].a != i || v.at(i).a != i ||
  216. cv.at(i).a != i) {
  217. printError(1);
  218. }
  219. }
  220. if(v.size() != elements) {
  221. printError(2);
  222. }
  223. }
  224. {
  225. V v1;
  226. v1.push_back(A(10));
  227. V v2 = v1;
  228. V v3;
  229. v3.push_back(A(20));
  230. v3 = v1;
  231. if(v1[0].a != 10 || v2[0].a != 10 || v3[0].a != 10) {
  232. printError(3);
  233. }
  234. V v4 = std::move(v1);
  235. V v5(std::move(v2));
  236. V v6;
  237. v6 = std::move(v3);
  238. if(v1.size() != 0 || v2.size() != 0 || v3.size() != 0 ||
  239. v4.size() != 1 || v5.size() != 1 || v6.size() != 1) {
  240. printError(4);
  241. }
  242. }
  243. {
  244. V v;
  245. v.resize(1, A(8));
  246. v.resize(3, A(9));
  247. if(v.size() != 3 || v[0].a != 8 || v[1].a != 9 || v[2].a != 9) {
  248. printError(5);
  249. }
  250. v.resize(1, A(10));
  251. if(v.size() != 1 || v[0].a != 8) {
  252. printError(6);
  253. }
  254. }
  255. {
  256. std::vector<A> v1;
  257. V v2;
  258. for(int i = 0; i < 200; i++) {
  259. v1.push_back(A(i));
  260. v2.push_back(A(i));
  261. }
  262. v1.erase(v1.begin());
  263. v1.erase(v1.begin() + 4, v1.begin() + 8);
  264. v1.erase(v1.begin() + 30, v1.begin() + 56);
  265. v2.erase(v2.begin());
  266. v2.erase(v2.begin() + 4, v2.begin() + 8);
  267. v2.erase(v2.begin() + 30, v2.begin() + 56);
  268. if(static_cast<int>(v1.size()) != static_cast<int>(v2.size())) {
  269. printError(100);
  270. } else {
  271. for(unsigned int i = 0; i < v1.size(); i++) {
  272. if(v1[i].a != v2[i].a) {
  273. printError(200);
  274. }
  275. }
  276. }
  277. }
  278. if(A::instances != 0) {
  279. std::cout << "object counter is not 0: " << A::instances << "\n";
  280. }
  281. }
  282. void test() {
  283. {
  284. Vector<A> v;
  285. v.push_back(1);
  286. v.push_back(2);
  287. v.push_back(3);
  288. v.erase_by_swap(1);
  289. if(v.size() != 2 || v[0].a != 1 || v[1].a != 3) {
  290. printError(9);
  291. }
  292. v.erase_by_swap(1);
  293. if(v.size() != 1 || v[0].a != 1) {
  294. printError(10);
  295. }
  296. v.erase_by_swap(0);
  297. if(v.size() != 0) {
  298. printError(11);
  299. }
  300. }
  301. if(A::instances != 0) {
  302. std::cout << "object counter is not 0: " << A::instances << "\n";
  303. }
  304. }
  305. int main() {
  306. test<Vector<A>>();
  307. test();
  308. std::cout << "--------------------------\n";
  309. test<std::vector<A>>();
  310. }