Más que strings, para programar BigInts la práctica más extendida es usar vectores de enteros, de forma que en cada posición no se guarda un único dígito, sino varios de ellos, permitiendo aprovechar las operaciones de las variables nativas del lenguaje y además haciendo el cálculo de forma mucho más eficiente.
He picado un ejemplo para poder hacer potencias. No lo he testeado así que quizá tenga algún bug, pero la idea en sí creo que se entiende con el código.
#include <iostream>
#include <vector>
#include <sstream>
#include <iomanip>
using namespace std;
typedef long long ll;
typedef vector<ll> VL;
const ll base = 1000000000;
class BigInt {
public:
VL v;
int signo;
BigInt() {
v = VL(1, 0);
signo = 1;
}
BigInt(ll x) {
do {
v.push_back(x%base);
x /= base;
} while (x > 0);
signo = 1;
}
void operator*=(const BigInt& b) {
BigInt c;
c.v.resize(v.size() + b.v.size(), 0);
c.signo = signo*b.signo;
for (int i = 0; i < v.size(); ++i) {
for (int j = 0; j < b.v.size(); ++j) {
int k = i + j;
c.v[k] += v[i]*b.v[j];
while (c.v[k] >= base) {
c.v[k + 1] += c.v[k]/base;
c.v[k++] %= base;
}
}
}
int i;
for (i = c.v.size() - 1; i > 0 and c.v[i] == 0; --i);
c.v.resize(i + 1);
*this = c;
}
BigInt pow(ll k) {
if (k == 0) return BigInt(1);
BigInt res = this->pow(k >> 1);
res *= res;
if (k&1) res *= (*this);
return res;
}
friend ostream &operator<<(ostream &out, BigInt &b) {
if (b.signo < 0) out << '-';
int i = b.v.size() - 1;
out << b.v[i];
for (--i; i >= 0; --i) out << setw(9) << setfill('0') << b.v[i];
return out;
}
};
int main() {
BigInt x(10);
x = x.pow(1000);
cout << x << endl;
}