I made this project to practice object-oriented programming. I have implemented operator+ and operator+=. The rationale behind struct cache are
- I don't necessarily need to traverse the entire container to display the value.
- Using a
struct cachemakes it possible to modify its value even in a const method
I also considered eliminating the container entirely and representing BigInt as a string, that might have been a better approach.
Here is my code
BigInt.h
#ifndef BIGINTEGER_H_
#define BIGINTEGER_H_
#include <iostream>
#include <deque>
#include <string>
namespace MyBigInteger
{
struct cache
{
bool has_changed{true};
std::string cache_value{""};
};
class BigInt
{
friend std::ostream& operator<<(std::ostream& os, const BigInt& bigint);
friend std::istream& operator>>(std::istream& is, BigInt& bigint);
public:
BigInt();
explicit BigInt(const std::string &str_number);
BigInt& operator+=(const BigInt& rhs);
BigInt operator+(const BigInt& lhs) const;
size_t size() const;
private:
cache* string_rep;
size_t bigint_size;
std::deque<int8_t> elems;
std::string get_new_cache_value() const;
};
int8_t to_digit(const char chr);
char to_char(const int value);
}
#endif
BigInt.cpp
#include "BigInt.h"
#include <algorithm>
namespace MyBigInteger
{
BigInt::BigInt()
: string_rep{new cache{false, ""}} {}
BigInt::BigInt(const std::string& str_number)
: string_rep{new cache{false, str_number}}, bigint_size{str_number.size()}
{
for(const auto chr : str_number)
elems.push_back(to_digit(chr));
}
int8_t to_digit(const char chr)
{
return chr - '0';
}
char to_char(const int value)
{
return value + '0';
}
std::ostream& operator<<(std::ostream& os, const BigInt& bigint)
{
if(bigint.string_rep->has_changed)
{
bigint.string_rep->cache_value = bigint.get_new_cache_value();
bigint.string_rep->has_changed = false;
}
os << bigint.string_rep->cache_value;
return os;
}
std::istream& operator>>(std::istream& is, BigInt& bigint)
{
is >> bigint.string_rep->cache_value;
return is;
}
std::string BigInt::get_new_cache_value() const
{
std::string temp_str;
std::for_each(elems.cbegin(), elems.cend(), [&](const int x)
{ temp_str += to_char(x); });
return temp_str;
}
BigInt& BigInt::operator+=(const BigInt& rhs)
{
*this = std::move(*this + rhs);
return *this;
}
BigInt BigInt::operator+(const BigInt& rhs) const
{
BigInt res;
res.string_rep = new cache{true, ""};
unsigned total = 0;
unsigned carry = 0;
auto beg = elems.crbegin(), rhs_beg = rhs.elems.crbegin();
for(; beg != elems.crend() && rhs_beg != rhs.elems.crend();
++beg, ++rhs_beg)
{
total = *beg + *rhs_beg + carry;
carry = total / 10;
res.elems.push_front(total % 10);
}
if(beg == elems.crend() && rhs_beg == rhs.elems.crend())
return res;
else if(beg == elems.crend())
{
for(; rhs_beg != rhs.elems.crend(); ++rhs_beg)
{
total = *rhs_beg + carry;
carry = total / 10;
res.elems.push_front(total % 10);
}
}
else
{
for(; beg != elems.crend(); ++beg)
{
total = *beg + carry;
carry = total / 10;
res.elems.push_front(total % 10);
}
}
if(carry)
res.elems.push_front(carry);
return res;
}
size_t BigInt::size() const
{
return bigint_size;
}
}
main.cpp
#include "BigInt.h"
#include <iostream>
using namespace MyBigInteger;
int main()
{
BigInt my_bigint{"123456789123456789123456789"};
BigInt my_bigint2{"24682468246824682468"};
std::cout << "my_bigint + my_bigint2: " << my_bigint + my_bigint2 << '\n';
my_bigint += my_bigint2;
std::cout << "my_bigint after my_bigint += my_bigint2: " << my_bigint << '\n';
BigInt my_bigint3{};
std::cout << "default my_bigint3: " << my_bigint3 << '\n';
std::cin >> my_bigint3;
std::cout << "my_bigint3 after input: " << my_bigint3 << '\n';
BigInt my_bigint4{my_bigint3};
std::cout << "After copy constructor: " << my_bigint4 << '\n';
my_bigint4 = my_bigint;
std::cout << "After copy assignment: " << my_bigint4 << '\n';
}
```