Compare commits
7 Commits
bbb64dd8b7
...
b778751254
Author | SHA1 | Date | |
---|---|---|---|
b778751254 | |||
84846a5619 | |||
49b158dafe | |||
560abcb201 | |||
2b62e37ccd | |||
3365d5986b | |||
d1fd4eb017 |
@ -4,5 +4,7 @@
|
|||||||
env = DefaultEnvironment(tools=['default', 'compilation_db'])
|
env = DefaultEnvironment(tools=['default', 'compilation_db'])
|
||||||
env.CompilationDatabase()
|
env.CompilationDatabase()
|
||||||
|
|
||||||
|
env.Append(CCFLAGS=["-Wall", "-Wextra", "-Werror", "-pedantic"])
|
||||||
|
|
||||||
# Build the output program
|
# Build the output program
|
||||||
Program('vector.out', Glob('*.cpp'))
|
Program('vector.out', Glob('*.cpp'))
|
||||||
|
70
Vector.h
70
Vector.h
@ -10,13 +10,14 @@ class Vector {
|
|||||||
// If the needed capacity is known, it can be passed; other wise, a default
|
// If the needed capacity is known, it can be passed; other wise, a default
|
||||||
// capacity is reserved.
|
// capacity is reserved.
|
||||||
Vector(unsigned int capacity = 10)
|
Vector(unsigned int capacity = 10)
|
||||||
: capacity(capacity), element_count(0), data(new T[capacity]) {
|
: capacity(capacity), element_count(0),
|
||||||
|
data((T *)::operator new(capacity * sizeof(T))) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy Constructor
|
// Copy Constructor
|
||||||
Vector(const Vector &other)
|
Vector(const Vector &other)
|
||||||
: capacity(other.capacity), element_count(other.element_count),
|
: capacity(other.capacity), element_count(other.element_count),
|
||||||
data(new T[capacity]) {
|
data((T *)::operator new(capacity * sizeof(T))) {
|
||||||
// `std::copy` is used because it is more flexible than `std::memcpy`,
|
// `std::copy` is used because it is more flexible than `std::memcpy`,
|
||||||
// and the compiler will replace it with `memcpy` anyway if appropriate,
|
// and the compiler will replace it with `memcpy` anyway if appropriate,
|
||||||
// so there is no performance loss.
|
// so there is no performance loss.
|
||||||
@ -24,7 +25,7 @@ class Vector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Move Constructor using the copy-and-swap-idiom
|
// Move Constructor using the copy-and-swap-idiom
|
||||||
Vector(Vector &&other) : data(new T[capacity]) {
|
Vector(Vector &&other) : data(nullptr) {
|
||||||
swap(*this, other);
|
swap(*this, other);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,7 +43,10 @@ class Vector {
|
|||||||
|
|
||||||
// Destructor
|
// Destructor
|
||||||
~Vector() {
|
~Vector() {
|
||||||
delete[] data;
|
for (unsigned int i = 0; i < element_count; i++) {
|
||||||
|
data[i].~T();
|
||||||
|
}
|
||||||
|
::operator delete(data, capacity * sizeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Bracket Operator for accessing elements
|
// Bracket Operator for accessing elements
|
||||||
@ -52,7 +56,7 @@ class Vector {
|
|||||||
|
|
||||||
// Equals Operator: Returns true if the number of elements is identical and
|
// Equals Operator: Returns true if the number of elements is identical and
|
||||||
// each of these elements are equal
|
// each of these elements are equal
|
||||||
bool operator==(const Vector &other) {
|
bool operator==(const Vector &other) const {
|
||||||
if (size() != other.size()) return false;
|
if (size() != other.size()) return false;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < size(); i++) {
|
for (unsigned int i = 0; i < size(); i++) {
|
||||||
@ -104,6 +108,22 @@ class Vector {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Erase an element by swapping the previously last element to its place
|
||||||
|
void erase_by_swap(unsigned int position) {
|
||||||
|
assert(position < element_count);
|
||||||
|
|
||||||
|
// Swap the last element to this position
|
||||||
|
std::swap(data[position], data[element_count - 1]);
|
||||||
|
|
||||||
|
// Delete the previous element at this position (now last)
|
||||||
|
data[element_count - 1].~T();
|
||||||
|
element_count--;
|
||||||
|
}
|
||||||
|
|
||||||
|
T *as_array() const {
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
// Returns the number of elements in the vector, regardless of the actually
|
// Returns the number of elements in the vector, regardless of the actually
|
||||||
// reserved memory.
|
// reserved memory.
|
||||||
unsigned int size() const {
|
unsigned int size() const {
|
||||||
@ -131,8 +151,9 @@ class Vector {
|
|||||||
|
|
||||||
// Resize the size of the vector to the given new_size
|
// Resize the size of the vector to the given new_size
|
||||||
// If this decreases the size, some elements are deleted
|
// If this decreases the size, some elements are deleted
|
||||||
// If this increases the size, some default-constructed elements are added
|
// If this increases the size, some default-constructed elements are added.
|
||||||
void resize(unsigned int new_size) {
|
// If no default constructor is available, a padder must be passed.
|
||||||
|
void resize(unsigned int new_size, const T &padder = T()) {
|
||||||
int difference = new_size - size();
|
int difference = new_size - size();
|
||||||
|
|
||||||
if (difference > 0) {
|
if (difference > 0) {
|
||||||
@ -140,13 +161,13 @@ class Vector {
|
|||||||
reallocate(new_size);
|
reallocate(new_size);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add additional default-constructed items
|
// Add additional items
|
||||||
for (int i = 0; i < difference; i++) {
|
for (int i = 0; i < difference; i++) {
|
||||||
data[element_count + i] = T();
|
new (data + element_count + i) T(padder);
|
||||||
}
|
}
|
||||||
} else if (difference < 0) {
|
} else if (difference < 0) {
|
||||||
// Call the destructor on all excess items
|
// Call the destructor on all excess items
|
||||||
for (int i = -1; i > difference; i--) {
|
for (int i = -1; i >= difference; i--) {
|
||||||
data[element_count + i].~T();
|
data[element_count + i].~T();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -159,15 +180,26 @@ class Vector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
unsigned int capacity; // Number of T objects the Vector could hold
|
||||||
|
unsigned int
|
||||||
|
element_count; // Number of T objects the Vector currently holds
|
||||||
T *data;
|
T *data;
|
||||||
unsigned int element_count;
|
|
||||||
unsigned int capacity;
|
|
||||||
|
|
||||||
bool is_using_excessive_memory() {
|
// Return a sensible capacity for the given size (including some
|
||||||
|
// padding).
|
||||||
|
unsigned int get_capacity_for_size(unsigned int size) const {
|
||||||
|
return size * 2 + 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return true if the Vector is holding much more memory (capacity) than it
|
||||||
|
// currently needs (element_count).
|
||||||
|
inline bool is_using_excessive_memory() const {
|
||||||
return 10 + element_count * 2 < capacity;
|
return 10 + element_count * 2 < capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool new_size_exceeds_capacity(unsigned int new_size) {
|
// Return true if the passed new_size would cause the Vector to exceed the
|
||||||
|
// current capacity (meaning it must be expanded with `reallocate` first).
|
||||||
|
inline bool new_size_exceeds_capacity(unsigned int new_size) const {
|
||||||
return new_size > capacity;
|
return new_size > capacity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -175,16 +207,10 @@ class Vector {
|
|||||||
// Note that this function doesn't validate its input for optimization
|
// Note that this function doesn't validate its input for optimization
|
||||||
// reasons!
|
// reasons!
|
||||||
void reallocate(unsigned int new_size) {
|
void reallocate(unsigned int new_size) {
|
||||||
T *new_data = new T[new_size];
|
T *new_data = (T *)::operator new(new_size * sizeof(T));
|
||||||
std::copy(data, data + element_count, new_data);
|
std::copy(data, data + element_count, new_data);
|
||||||
delete[] data;
|
::operator delete(data, capacity * sizeof(T));
|
||||||
|
|
||||||
data = new_data;
|
data = new_data;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return a sensible capacity for the given size (including some
|
|
||||||
// padding).
|
|
||||||
unsigned int get_capacity_for_size(unsigned int size) {
|
|
||||||
return size * 2 + 5;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
142
test.cpp
142
test.cpp
@ -14,12 +14,20 @@ SCENARIO("Vector size and length are correct", "[vector]") {
|
|||||||
}
|
}
|
||||||
|
|
||||||
SCENARIO("The bracket operator returns the correct element", "[vector]") {
|
SCENARIO("The bracket operator returns the correct element", "[vector]") {
|
||||||
|
GIVEN("A vector with some elements") {
|
||||||
Vector<int> v(20);
|
Vector<int> v(20);
|
||||||
v.push_back(1);
|
v.push_back(1);
|
||||||
v.push_back(2);
|
v.push_back(2);
|
||||||
v.push_back(3);
|
v.push_back(3);
|
||||||
|
|
||||||
|
THEN("The element in the middle must have the expected value") {
|
||||||
REQUIRE(v[1] == 2);
|
REQUIRE(v[1] == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("The same value must be accessible using as_array") {
|
||||||
|
REQUIRE(v.as_array()[1] == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SCENARIO("Equality is returned correctly", "[vector]") {
|
SCENARIO("Equality is returned correctly", "[vector]") {
|
||||||
@ -236,3 +244,137 @@ SCENARIO("Resizing a vector", "[vector]") {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SCENARIO("Non-default constructor", "[vector]") {
|
||||||
|
GIVEN("A class object with a non-default constructor") {
|
||||||
|
class NonDefault {
|
||||||
|
public:
|
||||||
|
NonDefault(int a) : a(a) {
|
||||||
|
}
|
||||||
|
|
||||||
|
int a;
|
||||||
|
};
|
||||||
|
|
||||||
|
WHEN("Creating a vector and adding an element") {
|
||||||
|
Vector<NonDefault> non_default_vector;
|
||||||
|
|
||||||
|
non_default_vector.push_back(NonDefault(1));
|
||||||
|
|
||||||
|
THEN("The element should be accessible as usual") {
|
||||||
|
REQUIRE(non_default_vector[0].a == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("Creating a vector and resizing it") {
|
||||||
|
Vector<NonDefault> non_default_vector;
|
||||||
|
|
||||||
|
non_default_vector.resize(50, NonDefault(42));
|
||||||
|
|
||||||
|
THEN("The new elements should correspond to the passed padder") {
|
||||||
|
REQUIRE(non_default_vector[49].a == 42);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SCENARIO("Erase by swap", "[vector]") {
|
||||||
|
GIVEN("A vector with some elements") {
|
||||||
|
Vector<int> v(3);
|
||||||
|
v.push_back(1);
|
||||||
|
v.push_back(2);
|
||||||
|
v.push_back(3);
|
||||||
|
|
||||||
|
WHEN("Erasing-by-swap the first element") {
|
||||||
|
v.erase_by_swap(0);
|
||||||
|
|
||||||
|
THEN("The size should have decrease by one") {
|
||||||
|
REQUIRE(v.size() == 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
THEN("The previously last element should now be the first") {
|
||||||
|
REQUIRE(v[0] == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int count = 0;
|
||||||
|
|
||||||
|
class ReferenceCounter {
|
||||||
|
public:
|
||||||
|
ReferenceCounter() {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceCounter(const ReferenceCounter &) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceCounter(ReferenceCounter &&) {
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceCounter &operator=(const ReferenceCounter &) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ReferenceCounter &operator=(ReferenceCounter &&) {
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
~ReferenceCounter() {
|
||||||
|
count--;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SCENARIO("Constructors and Destructors are called as expected", "[vector]") {
|
||||||
|
GIVEN("A vector with a custom reference-counting class") {
|
||||||
|
|
||||||
|
Vector<ReferenceCounter> v(3);
|
||||||
|
v.push_back(ReferenceCounter());
|
||||||
|
v.push_back(ReferenceCounter());
|
||||||
|
v.push_back(ReferenceCounter());
|
||||||
|
|
||||||
|
REQUIRE(count == 3);
|
||||||
|
|
||||||
|
WHEN("Erasing the first element") {
|
||||||
|
v.erase(0);
|
||||||
|
|
||||||
|
THEN("The reference count should have decreased") {
|
||||||
|
REQUIRE(count == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("Erasing by swap") {
|
||||||
|
v.erase_by_swap(0);
|
||||||
|
|
||||||
|
THEN("The reference count should have decreased") {
|
||||||
|
REQUIRE(count == 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("Resizing to 1") {
|
||||||
|
v.resize(1);
|
||||||
|
|
||||||
|
THEN("The reference count should be 1") {
|
||||||
|
REQUIRE(count == 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("Resizing to 10") {
|
||||||
|
v.resize(10);
|
||||||
|
|
||||||
|
THEN("The reference count should be 10") {
|
||||||
|
REQUIRE(count == 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
WHEN("Move-constructing another vector") {
|
||||||
|
Vector<ReferenceCounter> v2 = std::move(v);
|
||||||
|
|
||||||
|
THEN("The reference count should have remained the same") {
|
||||||
|
REQUIRE(count == 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user