The usage of "atoX" is not the way to go. Here you can and should use classical formatted input functions using operator >>
.
Then everything can be fairly easy.
Below you can see one of many many potential implementations. This could be used as a basic framework to build a class with more functionalities.
We will provide functions to allocate a 2d array and to free the memory later. ALso the inserter and extraction functions >>
and <<
will be overloaded. And finally we add the rule of 5 special functions. With that, we can store all read data blocks in a std::vector
.
Example:
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <vector>
struct DynamicMatrix {
// Basic data of a matrix
double** matrix{};
size_t numberOfRows{};
size_t numberOfColumns{};
// We do not want to create memory holes
void clear() {
for (size_t row{}; row < numberOfRows; ++row)
delete[] matrix[row];
delete[] matrix;
numberOfRows = {}; numberOfColumns = {};
}
// Simple extraction operator. Will read only one block of doubles from the stream
friend std::istream& operator >> (std::istream& is, DynamicMatrix& dm) {
// Free old memory and get number of rows and columns for this block
dm.clear();
is >> dm.numberOfRows >> dm.numberOfColumns;
// Get memory for row pointers
dm.matrix = new double* [dm.numberOfRows]{};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < dm.numberOfRows; ++row) {
dm.matrix[row] = new double[dm.numberOfColumns]{};
// And now read the column values
for (size_t col{}; col < dm.numberOfColumns; ++col)
is >> dm.matrix[row][col];
}
return is;
}
// Simple output
friend std::ostream& operator << (std::ostream& os, const DynamicMatrix& dm) {
for (size_t row{}; row < dm.numberOfRows; ++row) {
for (size_t col{}; col < dm.numberOfColumns; ++col)
os << dm.matrix[row][col] << '\t';
os << '\n';
}
return os;
}
// Rule of 5
// Default constructor (Elements are already initialized)
DynamicMatrix() {}
// Copy constructor
DynamicMatrix(const DynamicMatrix& dm) {
// Copy the size
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
// Get memory for row pointers
matrix = new double* [numberOfRows]{};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < numberOfRows; ++row) {
matrix[row] = new double[numberOfColumns]{};
// And now copy the data
for (size_t col{}; col < numberOfColumns; ++col)
matrix[row][col] = dm.matrix[row][col];
}
}
// Copy assignment
DynamicMatrix& operator = (const DynamicMatrix& dm) {
if (this != &dm) { // not a self-assignment
// Get rid of old values
clear();
// Copy the size
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
// Get memory for row pointers
matrix = new double* [numberOfRows] {};
// And now get the memory for the columns for all the rows
for (size_t row{}; row < numberOfRows; ++row) {
matrix[row] = new double[numberOfColumns] {};
// And now copy the data
for (size_t col{}; col < numberOfColumns; ++col)
matrix[row][col] = dm.matrix[row][col];
}
}
return *this;
}
// Move constructor
DynamicMatrix(DynamicMatrix&& dm) noexcept {
// Copy size and data
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
matrix = dm.matrix;
// Clear other matrix
dm.matrix = {};
dm.numberOfRows = {};
dm.numberOfColumns = {};
}
// Move assignment
DynamicMatrix& operator = (DynamicMatrix&& dm) noexcept{
if (this != &dm) { // not a self-assignment
clear();
// Copy size and data
numberOfRows = dm.numberOfRows;
numberOfColumns = dm.numberOfColumns;
matrix = dm.matrix;
// Clear other matrix
dm.matrix = {};
dm.numberOfRows = {};
dm.numberOfColumns = {};
}
return *this;
}
// Delete memory when going out of existence
virtual ~DynamicMatrix() {
clear();
}
};
// Testdata
std::istringstream iss{ R"(4 2
1.35477 8.35009
9.68868 2.21034
3.08167 5.47221
1.88382 9.92881
2 3
9.96461 9.67695 7.25839
9.8111 1.09862 7.98106
)" };
// Test function
int main() {
// Here we will store all dynamic matrices
std::vector<DynamicMatrix> blocks{};
// One temporary matrix
DynamicMatrix dmTemp{};
// Read all block from stream
while (iss >> dmTemp) {
blocks.push_back(std::move(dmTemp));
}
// Debug output
for (const DynamicMatrix& block : blocks) {
std::cout << "\n\n" << block;
}
}
If you want to dive in deeper, I show you also a full implementation of a 1d DynamicArray, from which you can of course also build a 2d Matrix by defining an array of arrays.
#include <iostream>
#include <sstream>
#include <initializer_list>
// -----------------------------------------------------------------------------------------------
// Definition of simple dynamic array class
template <typename T>
class DynamicArray {
// The Dynamic Array has an initial capacity.
// If more elements will be added, there will be a reallocation with double capacity
static constexpr unsigned int InitialCapacity{ 8 };
// Internal data ------------------------------------------------------------------------------
T* data{}; // Dynamic Storage for Data
unsigned int numberOfElements{}; // Number of elements currently in the container
unsigned int capacity{ InitialCapacity }; // Current maximum capacity of the container
public:
// Construction and Destruction ---------------------------------------------------------------
DynamicArray(); // Default constructor. Allocate new memory
DynamicArray(const unsigned int size); // Constructor for a given size. Allocate new memory
DynamicArray(const DynamicArray& other); // Copy constructor. Make a deep copy
DynamicArray(DynamicArray&& other); // Move constructor
// Special constructors
template <class Iterator> DynamicArray(Iterator begin, Iterator end); // Initialize from range
template <int N> DynamicArray(const T(&other)[N]); // Initialize from C_Sytle array,e.g. a string literal
template <int N> DynamicArray(T(&other)[N]);
DynamicArray(const std::initializer_list<T>& list); // Take data from initializer list
~DynamicArray(); // Destructor: Release previously allocated memory
// Housekeeping ---------------------------------------------------------------
bool empty() const; // Do we have elements in the container? Do not mix up with capacity
void clear(); // Clear will not delete anything. Just set element count to 0
unsigned int size() const; // How many elements are in the container
// Main working functions
void push_back(const T& d); // Add a new element at the end
// Operators for class------------------------ ---------------------------------------------------------------
T operator[] (const unsigned int i) const; // Index operator, get data at given index. No boundary check
T& operator[] (const unsigned int i); // Index operator, get data at given index. No boundary check
DynamicArray& operator=(const DynamicArray& other); // Assignment
DynamicArray& operator=(DynamicArray&& other); // Move Assignment
// Add iterator properties to class ---------------------------------------------------------------
class iterator { // Local class for iterator
T* iter{}; // This will be the iterator
T* begin{}; // For boundary check
T* end{}; // For boundary check
public: // Define alias names necessary for the iterator functionality
using iterator_category = std::random_access_iterator_tag;
using difference_type = std::ptrdiff_t;
using value_type = T;
using pointer = T*;
using reference = T&;
// Constructor
iterator(T* const i, T* const b, T* const e);
// Dereferencing
reference operator *() const;
pointer operator ->() const;
// Aithmetic operations
iterator& operator ++();
iterator& operator --();
iterator operator ++(int);
iterator operator --(int);
iterator operator +(const difference_type& n) const;
iterator& operator +=(const difference_type& n);
iterator operator -(const difference_type& n) const;
iterator& operator -=(const difference_type& n);
// Comparison
bool operator ==(const iterator& other) const { return iter == other.iter; };
bool operator !=(const iterator& other) const { return iter != other.iter; };
bool operator < (const iterator& other) const { return other.iter - iter < 0; };
bool operator <= (const iterator& other) const { return other.iter - iter <= 0; };
bool operator > (const iterator& other) const { return other.iter - iter > 0; };
bool operator >= (const iterator& other) const { return other.iter - iter >= 0; };
// Reference and difference
reference operator[] (const difference_type& n);
difference_type operator-(const iterator& other) const;
};
// Begin and end function to initialize an iterator
iterator begin() const;
iterator end() const;
// Working functions dealing with iterators. More may be added
iterator erase(iterator pos);
DynamicArray<T> split();
};
template <typename T>
DynamicArray<T> DynamicArray<T>::split() {
DynamicArray<T> result;
if (numberOfElements > 1) {
const size_t offset = numberOfElements / 2;
result.numberOfElements = numberOfElements - offset;
result.data = data + offset;
result.capacity = result.numberOfElements << 1;
numberOfElements = offset;
}
return result;
}
// Default constructor. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray() {
data = new T[capacity];
}
// Constructor for certain size. Allocate new memory
template <typename T>
inline DynamicArray<T>::DynamicArray(const unsigned int size) : data(new T[size]), numberOfElements(0), capacity(size) {
}
// Copy constructor
template <typename T>
DynamicArray<T>::DynamicArray(const DynamicArray& other) { // Copy constructor. Make a deep copy
capacity = numberOfElements = other.numberOfElements;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < other.numberOfElements; ++k)
data[k] = other.data[k]; // Copy data
}
// Move constructor
template <typename T>
DynamicArray<T>::DynamicArray(DynamicArray&& other) {
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
// Range constructor
template <typename T>
template <class Iterator>
DynamicArray<T>::DynamicArray(Iterator begin, Iterator end) {
data = new T[capacity];
for (Iterator i = begin; i != end; ++i)
push_back(*i);
}
// Construct from a const C-Style Array, like for example "Hello"
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(const T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from a C-Style Array
template <typename T>
template <int N>
DynamicArray<T>::DynamicArray(T(&other)[N]) {
capacity = numberOfElements = N;
data = new T[capacity]; // Get memory, same size as other container
for (size_t k = 0; k < N; ++k)
data[k] = other[k]; // Copy data
}
// Construct from an initializer list
template <typename T>
DynamicArray<T>::DynamicArray(const std::initializer_list<T>& list) {
data = new T[capacity];
for (const T& t : list) push_back(t);
}
// Destructor will release the dynamic allocated memory
template <typename T>
inline DynamicArray<T>::~DynamicArray() {
delete[] data;
} // Destructor: Release previously allocated memory
// Some houskeeping functions
template <typename T>
inline bool DynamicArray<T>::empty() const {
return numberOfElements == 0;
}
template <typename T>
inline void DynamicArray<T>::clear() {
numberOfElements = 0;
}; // Clear will not delete anything. Just set element count to 0
template <typename T>
inline unsigned int DynamicArray<T>::size() const {
return numberOfElements;
} // How many elements are in the container
// Main workhorse for a dynamic array.
// Store element, and alwaysprovide enough memory
template <typename T>
void DynamicArray<T>::push_back(const T& d) { // Add a new element at the end
if (numberOfElements >= capacity) { // Check, if capacity of this dynamic array is big enough
capacity *= 2; // Obviously not, we will double the capacity
T* temp = new T[capacity]; // Allocate new and more memory
for (unsigned int k = 0; k < numberOfElements; ++k)
temp[k] = data[k]; // Copy data from old memory to new memory
delete[] data; // Release old memory
data = temp; // And assign newly allocated memory to old pointer
}
data[numberOfElements++] = d; // And finally, store the given data at the end of the container
}
// Operators for class ------------------------ ---------------------------------------------------------------
template <typename T>
inline typename T DynamicArray<T>::operator[] (const unsigned int i) const {
return data[i];
} // Index operator, get data at given index. No boundary check
template <typename T>
inline typename T& DynamicArray<T>::operator[] (const unsigned int i) {
return data[i];
} // Index operator, get data at given index. No boundary check
// Assignement operator. Make a deep copy
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(const DynamicArray& other) {
if (this != &other) { // Prevent self-assignment
delete[] data; // Release any previosly existing memory
capacity = numberOfElements = other.numberOfElements;// Take over capacity and number of elements from other container
data = new T[capacity]; // Get new memory, depending on size of other
for (unsigned int k = 0; k < numberOfElements; ++k) // Copy other data
data[k] = other.data[k];
}
return *this;
}
template <typename T>
DynamicArray<T>& DynamicArray<T>::operator=(DynamicArray&& other) { // Move Assignment
if (this != &other) { // Prevent self-assignment
data = other.data;
numberOfElements = other.numberOfElements;
capacity = other.capacity;
other.capacity = InitialCapacity;
other.numberOfElements = 0;
other.data = new T[capacity];;
}
return *this;
}
// Implementation of iterator functions ---------------------------------------------------------------------
// COnstruction
template <typename T>
inline DynamicArray<T>::iterator::iterator(T* const i, T* const b, T* const e) : iter(i), begin(b), end(e) {
}; // Constructor for the iterator
// Dereferencing
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator *() const {
return *iter;
}
template <typename T>
inline typename DynamicArray<T>::iterator::pointer DynamicArray<T>::iterator::operator ->() const {
return iter;
}
// Arithmetic operations
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator ++() {
if (iter < end)
++iter;
return *this;
}
template <typename T>
inline typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator --() {
if (iter > begin)
--iter;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator ++(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter < end)
++(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator --(int) {
DynamicArray<T>::iterator tmp = *this;
if (this->iter > begin)
--(*this);
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator +(const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++tmp;
else
while (k++)
--tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator +=(const DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
++* this;
else
while (k++)
--* this;
return *this;
}
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::iterator::operator- (const DynamicArray<T>::iterator::difference_type& n) const {
DynamicArray<T>::iterator tmp = *this;
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--tmp;
else
while (k++)
++tmp;
return tmp;
}
template <typename T>
typename DynamicArray<T>::iterator& DynamicArray<T>::iterator::operator -=(const typename DynamicArray<T>::iterator::difference_type& n) {
DynamicArray<T>::iterator::difference_type k{ n };
if (k > 0)
while (k--)
--* this;
else
while (k++)
++* this;
return *this;
}
// Index operator for iterator
template <typename T>
inline typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator[] (const typename DynamicArray<T>::iterator::difference_type& n) {
return *(iter + n);
};
template <typename T>
typename DynamicArray<T>::iterator::reference DynamicArray<T>::iterator::operator-(const iterator& other) const {
difference_type result{};
int indexThis{ -1 }, indexOther{ -1 }, index{};
do {
;
if (nptr == iter)
indexThis = index;
if (nptr == other.iter)
indexOther = index;
++index;
} while (nptr != head);
if (indexThis >= 0 and indexOther >= 0)
result = indexThis - indexOther;
return result;
}
// Delta
template <typename T>
inline typename DynamicArray<T>::iterator::difference_type DynamicArray<T>::iterator::operator-(const typename DynamicArray<T>::iterator& other) const {
return iter - other.iter;
}
// ------------------------------------------------------------------------
// Get iterators for dynamic array
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::begin() const {
return iterator(data, data, data + numberOfElements);
}
template <typename T>
inline typename DynamicArray<T>::iterator DynamicArray<T>::end() const {
return iterator(data + numberOfElements, data, data + numberOfElements);
}
// ------------------------------------------------------------------------
// Any other functions for dynamic array
template <typename T>
typename DynamicArray<T>::iterator DynamicArray<T>::erase(typename DynamicArray<T>::iterator pos) {
iterator result{ pos };
if (pos != end()) {
while (pos != end()) {
*pos = *(pos + 1);
++pos;
}
++result;
--numberOfElements;
}
return result;
}
int main() {
DynamicArray<int> da1{ 1,2,3,4,5,6 };
DynamicArray<int> da2 = da1.split();
for (const int i : da1)
std::cout << i << ' ';
std::cout << "\n\n";
for (const int k : da1)
std::cout << k << ' ';
}