// File matrix.hpp
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;
};
// File matrix.hpp
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;
};
// File matrix.cpp
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)
{}
// File main.cpp
int main(int argc, char* argv[])
{
matrix m(2, 4);
std::cout << m.m_nb_rows << std::endl;
return 0;
}
# In the matrix folder:
mkdir build
cd build
cmake ..
make
or
# In the matrix folder:
cmmake . -B build
cmake --build build
Define methods that return m_nb_rows and m_nb_cols
// File matrix.hpp
class matrix
{
public:
matrix(std::size_t nb_rows, std::size_t nb_cols);
size_t nb_rows() const;
size_t nb_cols() const;
private:
std::size_t m_nb_rows;
std::size_t m_nb_cols;
std::vector<double> m_data;
};
// File matrix.cpp
size_t matrix::nb_rows() const
{
return m_nb_rows;
}
size_t matrix::nb_cols() const
{
return m_nb_cols;
}
int main(int argc, char* argv[])
{
hpc::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; // Error
return m_nb_rows;
}
Changing an attribute in a const method is forbidden
Define a method that resizes the matrix
class matrix
{
public:
matrix(std::size_t nb_rows, std::size_t nb_cols);
size_t nb_rows() const;
size_t nb_cols() const;
void resize(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;
};
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); // Error
return 0;
}
Calling a non-const method on a const object is forbidden
Define a constructor taking a single parameter (for squared matrices), and a default constructor
class matrix
{
public:
matrix();
matrix(std::size_t size);
matrix(std::size_t nb_rows, std::size_t nb_cols);
// ... as before
};
matrix::matrix()
: m_nb_rows(0),
m_nb_cols(0),
m_data()
{}
matrix::matrix(std::size_t size)
: m_nb_rows(size),
m_nb_cols(size),
m_data(size*size)
{}
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)
{}
class matrix
{
public:
matrix() = default; // Default constructor
matrix(std::size_t size);
matrix(std::size_t nb_rows, std::size_t nb_cols);
// ... as before
};
/* matrix::matrix()
: m_nb_rows(0),
m_nb_cols(0),
m_data()
{}*/
matrix::matrix(std::size_t size)
: matrix(size, size) // Delegating constructor
{}
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)
{}
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 matrix
{
public:
//matrix() = default; // Default constructor
matrix(std::size_t size = 0);
matrix(std::size_t nb_rows, std::size_t nb_cols);
// ... as before
};
// File main.cpp
int main(int argc, char* argv[])
{
hpc::matrix m = {{1., 2.}, {3., 4.}};
// ...
return 0;
}
class matrix
{
public:
matrix(std::size_t size);
matrix(std::size_t nb_rows, std::size_t nb_cols);
matrix(std::initializer_list<std::initializer_list<double>> init);
// ... as before
};
matrix::matrix(std::initializer_list<std::initializer_list<double>> init)
: m_nb_rows(init.size())
, m_nb_cols(init.size() ? init.begin()->size() : 0u)
, m_data(m_nb_rows, m_nb_cols)
{
auto dest = m_data.begin();
std::for_each(init.begin(), init.end(), [&dest](const auto& v)
{
dest = std::copy(v.begin(), v.end(), dest);
});
}
void compute(const matrix& m) { ... }
compute(4u);
// makes the compiler generate
matrix tmp(4u);
compute(tmp);
class matrix
{
public:
explicit matrix(std::size_t size);
matrix(std::size_t nb_rows, std::size_t nb_cols);
matrix(std::initializer_list<std::initializer_list<double>> init);
// ... as before
};
matrix m = {{ 1., 2.}, {3., 4.}};
std::cout << m[0, 1] << std::endl; // Illegal, operator[] can accept only one parameter
class matrix
{
public:
// ...
double& operator()(size_t i, size_t j);
const double& operator()(size_t i, 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];
}
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+(const matrix& lhs, double rhs)
{
matrix tmp(lhs);
tmp += rhs;
return tmp;
}
matrix operator+(double lhs, const matrix& rhs)
{
return rhs + lhs;
}
We want to convert a single-row matrix to a std::vector
class matrix
{
public:
// ...
// No return type
operator std::vector<double>() const { return m_data; }
};
class test
{
double m_value; // private by default
};
struct test
{
double m_value; // public by default
};
Plain Old Data (POD): structure with only data members, no methods
struct test
{
double m_value;
std::string m_name;
int m_id;
};
class connection
{
private:
int m_id;
};
void print_debug(const connection& c)
{
// Cannot access c.m_id because it is private
std::cout << c.m_id << std::endl;
}
class connection
{
private:
int m_id;
friend void print_debug(const connection&);
};
class connection
{
private;
int m_id;
friend class peer;
};
All methods defined in peer have access to connection::m_id
class peer
{
private:
int m_id;
};
class connection
{
public:
void print_peer(const peer& p)
{
// Illegal: peer::m_id is private
std::cout << p.m_id << std::end;
}
private:
int m_id;
friend class peer;
};
Friendship is not reciprocal
class connection
{
private:
int m_id;
friend class peer;
};
class peer
{
private:
friend class socket;
};
class socket
{
public:
void print_connection_id(const connection& c)
{
// Illegal: connection::m_id is private
std::cout << c.m_id << std::endl;
}
};
Friendship is not transitive
The goal of this TP is to implement a graph structure and the Dijkstra's algorithm (to find the shortest path from a node to another one)
Write a node class with:
#include <vector>
#include <utility>
namespace hpc
{
class node
{
public:
using neighboor = std::pair<size_t, double>;
using neighboor_list = std::vector<neightboor>;
explicit node(size_t id);
void add_neighboor(size_t id, double distance);
void add_neighboor(const neighboor& n);
const neightboor_list& get_neighboors() const;
private:
size_t m_id;
neighboor_list m_neighboor_list;
};
}
Write a graph class that holds a list of nodes. Provide a convenient API to add nodes and read them
namespace hpc
{
class graph
{
public:
using node_list = std::vector<node>;
void add_node(const node& n);
const node_list& get_nodes() const;
private:
node_list m_node_list;
};
}
The idea is to incrementally build a subgraph in which nodes are ordered by distance from the origin.
Input: G = (N, E)
P = {}
d[n] = +int for n in N
d[ninit] = 0
while there is a node out of P:
Choose n out of P such that d[n] is the smallest distance
Put n in P
For each b neighboor of n out of P:
if d[b] > d[n] + distance(b, n):
d[b] = d[n] + distance(b, n)
previous[b] = n
Input: G = (N, E)
Q = N
d[n] = +int for n in N
d[ninit] = 0
while Q not empty:
Choose n in Q such that d[n] is the smallest distance
remove n from Q
For each b neighboor of n in Q:
if d[b] > d[n] + distance(b, n):
d[b] = d[n] + distance(b, n)
previous[b] = n
Implement the Dijkstra'a algorithm. The function should have the following signature:
void dijkstra(const graph& g, size_t start, size_t end, std::list<size_t>& path);