@SylvainCorlay @JohanMabille @QuantStack
Update the repo
class matrix
{
public:
matrix(std::size_t nb_rows, std::size_t nb_cols);
private:
std::size_t m_nb_rows;
std::size_t m_nb_cols;
std::vector<double> m_data;
};
matrix::matrix(std::size_t nb_rows, std::size_t nb_cols)
: m_nb_rows(nb_rows),
m_nb_cols(nb_cols),
m_data(nb_rows * nb_cols)
{
}
int main(int argc, char* argv[])
{
dauphine::matrix m(2, 4);
std::cout << m.m_nb_rows << std::endl;
return 0;
}
Define methods that return m_nb_rows and m_nb_cols
class matrix
{
public:
matrix(std::size_t nb_rows, std::size_t nb_cols);
std::size_t nb_rows() const;
std::size_t nb_cols() const;
};
std::size_t matrix::nb_rows() const
{
return m_nb_rows;
}
std::size_t matrix::nb_cols() const
{
return m_nb_cols;
}
int main(int argc, char* argv[])
{
dauphine::matrix m(2, 4);
std::cout << m.nb_rows() << std::endl;
std::cout << m.nb_cols() << std::endl;
return 0;
}
std::size_t matrix::nb_rows() const
{
m_nb_rows = 0;
return m_nb_rows;
}
Changing an attribute in a const method is forbidden
Define a method that resizes the matrix
class matrix
{
public:
void resize(std::size_t nb_rows, std::size_t nb_cols);
};
void matrix::resize(std::size_t nb_rows, std::size_t nb_cols)
{
m_nb_rows = nb_rows;
m_nb_cols = nb_cols;
m_data.resize(m_nb_rows * m_nb_cols);
}
int main(int argc, char* argv[])
{
const matrix m(2, 4);
m.resize(3, 5);
return 0;
}
Calling a non-const method on a const object is forbidden
class matrix
{
public:
double& operator()(std::size_t i, std::size_t j);
const double& operator()(std::size_t i, std::size_t j) const;
}
double& matrix::operator()(std::size_t i, std::size_t j)
{
return m_data[i * m_nb_cols + j];
}
const double& matrix::operator()(std::size_t i, std::size_t j) const
{
return m_data[i * m_nb_cols + j];
}
#include <ostream>
std::ostream& operator<<(std::ostream& out, const matrix& m);
std::ostream& operator<<(std::ostream& out, const matrix& m)
{
for(std::size_t i = 0; i < m.nb_rows(); ++i)
{
for(std::size_t j = 0; j < m.nb_cols(); ++j)
{
out << m(i, j) << ", ";
}
out << std::endl;
}
return out;
}
class matrix
{
public:
matrix& operator+=(const matrix& rhs);
matrix& operator-=(const matrix& rhs);
matrix& operator*=(const matrix& rhs);
matrix& operator/=(const matrix& rhs);
};
matrix& matrix::operator+=(const matrix& rhs)
{
for(std::size_t i = 0; i < m_nb_rows; ++i)
{
for(std::size_t j = 0; j < m_nb_cols; ++j)
{
m_data[i * m_nb_cols + j] = rhs.m_data[i * m_nb_cols + j];
}
}
return *this;
}
matrix& matrix::operator+=(const matrix& rhs)
{
std::transform(m_data.begin(), m_data.end(), rhs.m_data.begin(),
m_data.begin(), std::plus<double>());
return *this;
}
class matrix
{
public:
matrix& operator+=(double rhs);
matrix& operator-=(double rhs);
matrix& operator*=(double rhs);
matrix& operator/=(double rhs);
};
matrix& matrix::operator+=(double rhs)
{
std::transform(m_data.begin(), m_data.end(), m_data.begin(),
[rhs](double arg) { return arg + rhs; });
return *this;
}
Solution 1 (bad)
class matrix
{
public:
matrix operator+(const matrix& rhs) const;
matrix operator+(double rhs) const;
};
matrix res = m1 + m2; // OK - equivalent to res = m1.operator+(m2);
matrix res = m1 + 2. // OK - equivalent to res = m1.operator+(2.);
matrix res = 2. + m1 // Error, no overload found for operator+(double, matrix)
Solution 2 (good)
class matrix
{
// ...
};
matrix operator+(const matrix& lhs, const matrix& rhs);
matrix operator+(const matrix& lhs, double rhs);
matrix operator+(double lhs, const matrix& rhs);
matrix operator+(const matrix& lhs, const matrix& rhs)
{
matrix tmp(lhs);
tmp += rhs;
return tmp;
}
matrix operator+(double lhs, const matrix& rhs)
{
return rhs + lhs;
}
class matrix
{
public:
matrix(std::size_t nb_rows, std::size_t nb_cols);
~matrix();
matrix(const matrix&);
matrix& operator=(const matrix&);
matrix(const matrix&&);
matrix& operator=(matrix&&);
};
matrix::matrix(const matrix& rhs)
: m_nb_rows(rhs.m_nb_rows),
m_nb_cols(rhs.m_nb_cols),
m_data(rhs.m_data)
{
}
std::vector
matrix matrix::sigmoid(const matrix& m)
{
matrix res(m.nb_rows(), m.nb_cols());
std::transform(m.m_data.begin(), m.m_data.end(), res.m_data.begin()
[](double arg) { return 1. / (1. + std::exp(-arg)); });
}
double m_data[N];
// N must be known during compilation
// N is not mutable, you cannot resize m_data;
double* p_data = new double[size];
// size can be computed at runtime
delete[] p_data; // Never forget this
delete[] p_data; // Never do it twice on a non null pointer
p_data = nullptr;
delete[] p_data; // OK
The survival guide to dynamic allocation
class uvector
{
public:
uvector(std::size_t size);
uvector(std::size_t size, double value);
private:
double* p_data;
std::size_t m_size;
};
uvector::uvector(std::size_t size)
: p_data(nullptr), m_size(0)
{
if(size != 0)
{
p_data = new double[size];
m_size = size;
}
}
uvector::uvector(std::size_t size, double value)
: p_data(nullptr), m_size(0)
{
if(size != 0)
{
p_data = new double[size];
m_size = size;
}
std::fill(p_data, p_data + size, value);
}
uvector::uvector(std::size_t size, double value)
: uvector(size) // delegating constructor
{
std::fill(p_data, p_data + size, value);
}
class test
{
public:
test(int i, int j) : m_i(i), m_j(j) {}
test(int i) : test(i, 0) {} // OK
test(int i) : test(i, 0), m_d1(0.), m_d2(0.) {} // Illegal
test(double d1, double d2) : m_d1(d1), m_d2(d2) {}
test(double d) : test(d, 0.) {} // Illegal, only one delegating constructor per class
private:
int m_i, m_j;
double m_d1, m_d2;
};
class uvector
{
public:
uvector() : uvector(0) {}
};
class uvector
{
public:
uvector(std::size_t size = 0);
};
class uvector
{
public:
uvector(std::size_t size = 0);
uvector(std::size_t size, double value);
~uvector();
};
uvector::~uvector()
{
delete[] p_data;
p_data = nullptr;
m_size = 0;
}
double* p1 = new double(size);
double* p2 = p1;
delete p1;
p2[2] // crash
class uvector
{
public:
uvector(std::size_t size = 0);
uvector(std::size_t size, double value);
~uvector();
uvector(const uvector& rhs);
};
uvector::uvector(const uvector& rhs)
: p_data(nullptr), m_size(0)
{
if(rhs.m_size != 0)
{
p_data = new double[rhs.m_size];
m_size = rhs.m_size;
std::copy(rhs.p_data, rhs.p_data + m_size, p_data);
}
}
class uvector
{
public:
uvector(std::size_t size = 0);
uvector(std::size_t size, double value);
~uvector();
uvector(const uvector& rhs);
uvector& operator=(const uvector& rhs);
};
uvector& uvector::operator=(const uvector& rhs)
{
delete[] p_data;
p_data = new double[rhs.m_size];
std::copy(rhs.p_data, rhs.p_data + rhs.m_size, p_data);
m_size = rhs.m_size;
return *this;
}
uvector v(2, 2.);
v = v;
Copy and swap idiom
uvector& uvector::operator=(const uvector& rhs)
{
double* tmp = new double[rhs.m_size]; // Always allocate a temporary
std::copy(rhs.p_data, rhs.p_data + rhs.m_size, tmp); // copy
std::swap(tmp, p_data); // then swap
delete[] tmp; // then delete the temporary
m_size = rhs.m_size;
return *this;
}
class uvector
{
public:
void swap(uvector& rhs);
private:
double* p_data;
std::size_t m_size;
};
void swap(uvector& lhs, uvector& rhs);
void uvector::swap(uvector& rhs)
{
using std::swap;
swap(p_data, rhs.p_data);
swap(m_size, rhs.m_size);
}
void swap(uvector& lhs, uvector& rhs)
{
lhs.swap(rhs);
}
Copy and swap idiom
uvector& uvector::operator=(const uvector& rhs)
{
uvector tmp(rhs);
swap(*this, tmp);
return *this;
}
uvector compute(const uvector& param)
{
uvector res;
// do some computation ...
return res;
}
// Inefficient copy
uvector res = compute(my_huge_param);
uvector& compute(const uvector& param)
{
uvector res;
// do some computation ...
return res; // Binding a temporary to a non const reference!
}
// dangling reference
uvector& res = compute(my_huge_param);
uvector compute(const uvector& param)
{
uvector res;
// do some computation ...
return res;
}
// rvalue reference
uvector&& res = compute(my_huge_param);
lvalue
rvalue
uvector& func(uvector& t)
{
// do some stuff
return t;
}
uvector t, something;
func(t) = something;
uvector compute(const huge_param& param);
void function(const uvector& param);
void function(uvector&& param);
uvector m;
function(m);
function(compute(m));
uvector&& ref = compute(m);
function(ref);
uvector compute(const huge_param& param);
void function(const uvector& param);
void function(uvector&& param);
uvector m;
function(m); // calls function(const uvector& param);
function(compute(m)); // calls function(uvector&& param);
uvector&& ref = compute(m);
function(ref); // calls function(const uvector& param);
function(static_cast<uvector&&>(ref));
function(std::move(uvector));
class uvector
{
public:
uvector(std::size_t = 0);
uvector(std::size_t size, double value);
~uvector();
uvector(const uvector&)
uvector& operator=(const uvector& rhs);
uvector(uvector&&);
uvector& operator=(uvector&& rhs);
};
uvector::uvector(uvector&& rhs)
: p_data(std::move(p_data)), m_size(std::move(rhs.m_size))
{
}
uvector::uvector(uvector&& rhs)
: p_data(std::move(p_data)), m_size(std::move(rhs.m_size))
{
rhs.p_data = nullptr;
rhs.m_size = 0;
}
uvector& uvector::operator=(uvector&& rhs)
{
using std::swap;
swap(p_data, rhs.p_data);
swap(m_size, rhs.m_size);
return *this;
}
If you need to declare and implement any of
... you probably need to implement all off them
class uvector
{
public:
uvector(std::size_t size = 0);
// ...
};
void compute(const uvector& v);
compute(std::size_t(2));
class uvector
{
public:
explicit uvector(std::size_t size = 0);
// ...
};
void compute(const uvector& v);
compute(std::size_t(2)); // Error: implicit conversion forbidden, constructor is declared as explicit
class optional
{
public:
optional(double value, bool has_value = true);
private:
double m_value;
bool m_has_value;
};
bool has_value(const optional& o) { return o.has_value(); }
bool res = has_value(1.2);
optional opt(1.2);
// ...
double res = opt;
class optional
{
public:
operator double() const { return m_value; }
// .... as previous
};
class Test
{
double m_value; // private by default
};
struct Test2
{
double m_value; // public by default
};
class uvector
{
private:
double* p_data;
std::size_t m_size;
};
void print_debug(const uvector& v)
{
// Cannot access v.p_data because it is private
std::cout << v.p_data << std::endl;
}
class uvector
{
private:
double* p_data;
std::size_t m_size;
friend void print_debug(const uvector&);
};
class uvector
{
private:
double* p_data;
std::size_t m_size;
friend class B;
};