Main.cpp 8.9 KB

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