Main.cpp 9.1 KB

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