Loading lang_cpp_05_alloc...
{ value_oper: '*', left: { value_oper: 10.1, left: { }, right: { } }, right: { value_oper: '+', left: { value_oper: 5.2, left: { }, right: { } }, right: { value_oper: 3.4, left: { }, right: { } } } }
struct ImpossibleNode { double value_oper; ImpossibleNode left; ImpossibleNode right; };
sizeof(ImpossibleNode)>=sizeof(double)+2*sizeof(ImpossibleNode)(voir cette section du cours S3-PRC) ne peut en aucun cas être respectée !
struct PossibleNode { double value_oper; PossibleNode *left; PossibleNode *right; };
class RawNode { public:
RawNode(double value);
RawNode(char oper, RawNode *left, RawNode *right);
std::string show() const;
double eval() const;
private: double value_oper_; RawNode *left_; RawNode *right_; };
RawNode::RawNode(double value) : value_oper_{value} , left_{nullptr} , right_{nullptr} { // nothing more to be done }
RawNode::RawNode(char oper, RawNode *left, RawNode *right) : value_oper_{double(oper)} , left_{left} , right_{right} { if(!left_||!right_) { throw std::runtime_error{"two child nodes expected"}; } }
std::string RawNode::show() const { std::string result; if(left_&&right_) // child nodes, assume an operation { const auto oper=char(value_oper_); result+='('; result+=left_->show(); result+=oper; result+=right_->show(); result+=')'; } else // no child nodes, assume a value { result+=std::to_string(value_oper_); } return result; }
double RawNode::eval() const { if(left_&&right_) // child nodes, assume an operation { const auto l=left_->eval(); const auto r=right_->eval(); const auto oper=char(value_oper_); switch(oper) { case '+': return l+r; case '-': return l-r; case '*': return l*r; case '/': return l/r; } throw std::runtime_error{"unexpected operation "+oper}; } else // no child nodes, assume a value { return value_oper_; } }
(RawNode *)0pour exprimer un tel pointeur nul.
// WARNING: never use malloc() in real C++ code! RawNode *n=(RawNode *)std::malloc(sizeof(RawNode)); // only allocate!
// WARNING: never use new in real C++ code! RawNode *n=new RawNode{10.1}; // allocate and initialise
using Node = s4prc::RawNode; // WARNING: never use new in real C++ code! auto *expr=new Node{'*', new Node{10.1}, new Node{'+', new Node{5.2}, new Node{3.4}}}; std::cout << expr->show() << "=" << expr->eval() << '\n';
// WARNING: never use new in real C++ code! auto *raw=new s4prc::Noisy<std::string>{"raw-pointer"}; std::cout << "raw memorises the address " << raw << '\n'; if(raw) { std::cout << "... which contains " << *raw << '\n'; }
//----------------------------------------------------------------------------
#ifndef S4PRC_NOISY_HPP #define S4PRC_NOISY_HPP
#include <iostream> #include <string> #include <vector>
namespace s4prc {
inline std::ostream & operator<<(std::ostream &output, const std::string &s) // display string within quotes { using std::operator<<; output << '"'; if(const auto sz=size(s); sz>20) // prevent from using too much space { output << s.substr(0, 10) << "..." << s.substr(sz-10); } else { output << s; } return output << '"'; }
template<typename E> inline std::ostream & operator<<(std::ostream &output, const std::vector<E> &v) // display vector within braces { output << '{'; if(const auto sz=size(v); sz>4) // prevent from using too much space { output << v[0] << ',' << v[1] << ",...," << v[sz-2] << ',' << v[sz-1]; } else { int count=0; for(const auto &elem: v) { if(count++) { output << ','; } output << elem; } } return output << '}'; }
template<typename T> struct Noisy : T { Noisy() : T{} { std::cout << "construct " << *this << " from nothing\n"; }
Noisy(const T &t) : T{t} { std::cout << "copy-construct " << *this << "\n ... from " << t << '\n'; }
Noisy(T &&t) : T{std::move(t)} { std::cout << "move-construct " << *this << "\n ... from (moved) " << t << '\n'; }
Noisy(const Noisy &n) : T{static_cast<const T &>(n)} { std::cout << "copy-construct " << *this << "\n ... from " << n << '\n'; }
Noisy(Noisy &&n) noexcept : T{std::move(static_cast<T &&>(n))} { std::cout << "move-construct " << *this << "\n ... from (moved) " << n << '\n'; }
Noisy & operator=(const T &t) { T::operator=(t); std::cout << "copy-assign " << *this << "\n ... from " << t << '\n'; return *this; }
Noisy & operator=(T &&t) { T::operator=(std::move(t)); std::cout << "move-assign " << *this << "\n ... from (moved) " << t << '\n'; return *this; }
Noisy & operator=(const Noisy &n) { T::operator=(static_cast<const T &>(n)); std::cout << "copy-assign " << *this << "\n ... from " << n << '\n'; return *this; }
Noisy & operator=(Noisy &&n) noexcept { T::operator=(std::move(static_cast<T &&>(n))); std::cout << "move-assign " << *this << "\n ... from (moved) " << n << '\n'; return *this; }
~Noisy() { std::cout << "destroy " << *this << '\n'; }
friend std::ostream & operator<<(std::ostream &output, const Noisy &n) { return output << "Noisy@" << &n << ":" << static_cast<const T &>(n); } };
} // namespace s4prc
#endif // S4PRC_NOISY_HPP
//----------------------------------------------------------------------------
s4prc::Noisy<std::string>et l'initialise avec un message explicite.
// WARNING: never use delete in real C++ code! delete raw;
// WARNING: never use delete in real C++ code! delete expr;
RawNode::~RawNode() { // WARNING: never use delete in real C++ code! // mandatory to prevent memory-leak! delete left_; // no effect on nullptr delete right_; // no effect on nullptr }
//-- disable copy -- RawNode(const RawNode &) =delete; RawNode & operator=(const RawNode &) =delete;
//-- disable move -- RawNode(RawNode &&) =delete; RawNode & operator=(RawNode &&) =delete;
//-- explicitely release internal resources -- ~RawNode();
<memory>, consiste avant tout à se comporter comme un pointeur sur une donnée de type T.
auto smart=std::make_unique<s4prc::Noisy<std::string>>("smart-pointer"); std::cout << "smart memorises the address " << smart.get() << '\n'; if(smart) { std::cout << "... which contains " << *smart << '\n'; }
<memory>, alloue dynamiquement une donnée de type T, l'initialise selon les paramètres qui lui sont transmis et renvoie un smart-pointer de type std::unique_ptr<T> qui désigne cette donnée.
std::unique_ptr<s4prc::Noisy<std::string>>.
auto smarter=smart; // attempt to copy a smart-pointer std::cout << "smarter memorises the address " << smarter.get() << '\n'; if(smarter) { std::cout << "... which contains " << *smarter << '\n'; } std::cout << "then smart memorises the address " << smart.get() << '\n'; if(smart) { std::cout << "... which contains " << *smart << '\n'; }
auto smarter=std::move(smart); // std::unique_ptr<T> is only movable
class SmartNode { public:
SmartNode(double value);
SmartNode(char oper, std::unique_ptr<SmartNode> left, std::unique_ptr<SmartNode> right);
std::string show() const;
double eval() const;
private: double value_oper_; std::unique_ptr<SmartNode> left_; std::unique_ptr<SmartNode> right_; };
using Node = s4prc::SmartNode; auto expr=std::make_unique<Node>('*', std::make_unique<Node>(10.1), std::make_unique<Node>('+', std::make_unique<Node>(5.2), std::make_unique<Node>(3.4)));
//----------------------------------------------------------------------------
#ifndef S4PRC_NOISY_HPP #define S4PRC_NOISY_HPP
#include <iostream> #include <string> #include <vector>
namespace s4prc {
inline std::ostream & operator<<(std::ostream &output, const std::string &s) // display string within quotes { using std::operator<<; output << '"'; if(const auto sz=size(s); sz>20) // prevent from using too much space { output << s.substr(0, 10) << "..." << s.substr(sz-10); } else { output << s; } return output << '"'; }
template<typename E> inline std::ostream & operator<<(std::ostream &output, const std::vector<E> &v) // display vector within braces { output << '{'; if(const auto sz=size(v); sz>4) // prevent from using too much space { output << v[0] << ',' << v[1] << ",...," << v[sz-2] << ',' << v[sz-1]; } else { int count=0; for(const auto &elem: v) { if(count++) { output << ','; } output << elem; } } return output << '}'; }
template<typename T> struct Noisy : T { Noisy() : T{} { std::cout << "construct " << *this << " from nothing\n"; }
Noisy(const T &t) : T{t} { std::cout << "copy-construct " << *this << "\n ... from " << t << '\n'; }
Noisy(T &&t) : T{std::move(t)} { std::cout << "move-construct " << *this << "\n ... from (moved) " << t << '\n'; }
Noisy(const Noisy &n) : T{static_cast<const T &>(n)} { std::cout << "copy-construct " << *this << "\n ... from " << n << '\n'; }
Noisy(Noisy &&n) noexcept : T{std::move(static_cast<T &&>(n))} { std::cout << "move-construct " << *this << "\n ... from (moved) " << n << '\n'; }
Noisy & operator=(const T &t) { T::operator=(t); std::cout << "copy-assign " << *this << "\n ... from " << t << '\n'; return *this; }
Noisy & operator=(T &&t) { T::operator=(std::move(t)); std::cout << "move-assign " << *this << "\n ... from (moved) " << t << '\n'; return *this; }
Noisy & operator=(const Noisy &n) { T::operator=(static_cast<const T &>(n)); std::cout << "copy-assign " << *this << "\n ... from " << n << '\n'; return *this; }
Noisy & operator=(Noisy &&n) noexcept { T::operator=(std::move(static_cast<T &&>(n))); std::cout << "move-assign " << *this << "\n ... from (moved) " << n << '\n'; return *this; }
~Noisy() { std::cout << "destroy " << *this << '\n'; }
friend std::ostream & operator<<(std::ostream &output, const Noisy &n) { return output << "Noisy@" << &n << ":" << static_cast<const T &>(n); } };
} // namespace s4prc
#endif // S4PRC_NOISY_HPP
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#ifndef S4PRC_RAW_NODE_HPP #define S4PRC_RAW_NODE_HPP
#include <string>
namespace s4prc {
class RawNode { public:
RawNode(double value);
RawNode(char oper, RawNode *left, RawNode *right);
//-- disable copy -- RawNode(const RawNode &) =delete; RawNode & operator=(const RawNode &) =delete;
//-- disable move -- RawNode(RawNode &&) =delete; RawNode & operator=(RawNode &&) =delete;
//-- explicitely release internal resources -- ~RawNode();
std::string show() const;
double eval() const;
private: double value_oper_; RawNode *left_; RawNode *right_; };
} // namespace s4prc
#endif // S4PRC_RAW_NODE_HPP
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#include "raw_node.hpp" #include <stdexcept>
namespace s4prc {
RawNode::RawNode(double value) : value_oper_{value} , left_{nullptr} , right_{nullptr} { // nothing more to be done }
RawNode::RawNode(char oper, RawNode *left, RawNode *right) : value_oper_{double(oper)} , left_{left} , right_{right} { if(!left_||!right_) { throw std::runtime_error{"two child nodes expected"}; } }
RawNode::~RawNode() { // WARNING: never use delete in real C++ code! // mandatory to prevent memory-leak! delete left_; // no effect on nullptr delete right_; // no effect on nullptr }
std::string RawNode::show() const { std::string result; if(left_&&right_) // child nodes, assume an operation { const auto oper=char(value_oper_); result+='('; result+=left_->show(); result+=oper; result+=right_->show(); result+=')'; } else // no child nodes, assume a value { result+=std::to_string(value_oper_); } return result; }
double RawNode::eval() const { if(left_&&right_) // child nodes, assume an operation { const auto l=left_->eval(); const auto r=right_->eval(); const auto oper=char(value_oper_); switch(oper) { case '+': return l+r; case '-': return l-r; case '*': return l*r; case '/': return l/r; } throw std::runtime_error{std::string{"unexpected operation "}+oper}; } else // no child nodes, assume a value { return value_oper_; } }
} // namespace s4prc
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#ifndef S4PRC_SMART_NODE_HPP #define S4PRC_SMART_NODE_HPP
#include <string> #include <memory>
namespace s4prc {
class SmartNode { public:
SmartNode(double value);
SmartNode(char oper, std::unique_ptr<SmartNode> left, std::unique_ptr<SmartNode> right);
std::string show() const;
double eval() const;
private: double value_oper_; std::unique_ptr<SmartNode> left_; std::unique_ptr<SmartNode> right_; };
} // namespace s4prc
#endif // S4PRC_SMART_NODE_HPP
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#include "smart_node.hpp" #include <stdexcept>
namespace s4prc {
SmartNode::SmartNode(double value) : value_oper_{value} , left_{} , right_{} { // nothing more to be done }
SmartNode::SmartNode(char oper, std::unique_ptr<SmartNode> left, std::unique_ptr<SmartNode> right) : value_oper_{double(oper)} , left_{std::move(left)} // std::unique_ptr<T> is only movable (no copy) , right_{std::move(right)} // std::unique_ptr<T> is only movable (no copy) { if(!left_||!right_) { throw std::runtime_error{"two child nodes expected"}; } }
std::string SmartNode::show() const { std::string result; if(left_&&right_) // child nodes, assume an operation { const auto oper=char(value_oper_); result+='('; result+=left_->show(); result+=oper; result+=right_->show(); result+=')'; } else // no child nodes, assume a value { result+=std::to_string(value_oper_); } return result; }
double SmartNode::eval() const { if(left_&&right_) // child nodes, assume an operation { const auto l=left_->eval(); const auto r=right_->eval(); const auto oper=char(value_oper_); switch(oper) { case '+': return l+r; case '-': return l-r; case '*': return l*r; case '/': return l/r; } throw std::runtime_error{std::string{"unexpected operation "}+oper}; } else // no child nodes, assume a value { return value_oper_; } }
} // namespace s4prc
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#ifndef S4PRC_COMPACT_NODE_HPP #define S4PRC_COMPACT_NODE_HPP
#include <string> #include <vector>
namespace s4prc {
class CompactNode { public:
CompactNode(double value);
CompactNode(char oper, int left, int right);
std::string show(const std::vector<CompactNode> &nodes) const;
double eval(const std::vector<CompactNode> &nodes) const;
private:
double value_oper_; int left_; int right_; };
template<typename ...Args> inline int // node index make(std::vector<CompactNode> &nodes, Args &&...args) { nodes.emplace_back(std::forward<Args>(args)...); return int(size(nodes))-1; }
} // namespace s4prc
#endif // S4PRC_COMPACT_NODE_HPP
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#include "compact_node.hpp" #include <stdexcept>
namespace s4prc {
CompactNode::CompactNode(double value) : value_oper_{value} , left_{-1} , right_{-1} { // nothing more to be done }
CompactNode::CompactNode(char oper, int left, int right) : value_oper_{double(oper)} , left_{left} , right_{right} { if(left_<0||right_<0) { throw std::runtime_error{"two child nodes expected"}; } }
std::string CompactNode::show(const std::vector<CompactNode> &nodes) const { std::string result; if(left_>=0&&right_>=0) // child nodes, assume an operation { const auto oper=char(value_oper_); result+='('; result+=nodes[left_].show(nodes); result+=oper; result+=nodes[right_].show(nodes); result+=')'; } else // no child nodes, assume a value { result+=std::to_string(value_oper_); } return result; }
double CompactNode::eval(const std::vector<CompactNode> &nodes) const { if(left_>=0&&right_>=0) // child nodes, assume an operation { const auto l=nodes[left_].eval(nodes); const auto r=nodes[right_].eval(nodes); const auto oper=char(value_oper_); switch(oper) { case '+': return l+r; case '-': return l-r; case '*': return l*r; case '/': return l/r; } throw std::runtime_error{std::string{"unexpected operation "}+oper}; } else // no child nodes, assume a value { return value_oper_; } }
} // namespace s4prc
//----------------------------------------------------------------------------
//----------------------------------------------------------------------------
#include <iostream> #include <chrono>
#include "noisy.hpp" #include "raw_node.hpp" #include "smart_node.hpp" #include "compact_node.hpp"
double // seconds (1e-6 precision) since 1970/01/01 00:00:00 UTC get_time() { const auto now=std::chrono::system_clock::now().time_since_epoch(); const auto us=std::chrono::duration_cast<std::chrono::microseconds>(now); return 1e-6*double(us.count()); }
void test_alloc() { std::cout << "\n~~~~ enter " << __func__ << "() ~~~~\n"; // WARNING: never use new in real C++ code! auto *raw=new s4prc::Noisy<std::string>{"raw-pointer"}; std::cout << "raw memorises the address " << raw << '\n'; if(raw) { std::cout << "... which contains " << *raw << '\n'; } // WARNING: never use delete in real C++ code! delete raw; // mandatory to prevent memory-leak! auto smart=std::make_unique<s4prc::Noisy<std::string>>("smart-pointer"); std::cout << "smart memorises the address " << smart.get() << '\n'; if(smart) { std::cout << "... which contains " << *smart << '\n'; } // auto smarter=smart; // std::unique_ptr<T> cannot be copied! auto smarter=std::move(smart); // std::unique_ptr<T> is only movable std::cout << "smarter memorises the address " << smarter.get() << '\n'; if(smarter) { std::cout << "... which contains " << *smarter << '\n'; } std::cout << "then smart memorises the address " << smart.get() << '\n'; if(smart) { std::cout << "... which contains " << *smart << '\n'; } std::cout << "~~~~ leave " << __func__ << "() ~~~~\n"; }
void test_RawNode() { std::cout << "\n~~~~ enter " << __func__ << "() ~~~~\n"; using Node = s4prc::RawNode; // WARNING: never use new in real C++ code! auto *expr=new Node{'*', new Node{10.1}, new Node{'+', new Node{5.2}, new Node{3.4}}}; std::cout << expr->show() << "=" << expr->eval() << '\n'; // WARNING: never use delete in real C++ code! delete expr; // mandatory to prevent memory-leaks! std::cout << "~~~~ leave " << __func__ << "() ~~~~\n"; }
void test_SmartNode() { std::cout << "\n~~~~ enter " << __func__ << "() ~~~~\n"; using Node = s4prc::SmartNode; auto expr=std::make_unique<Node>('*', std::make_unique<Node>(10.1), std::make_unique<Node>('+', std::make_unique<Node>(5.2), std::make_unique<Node>(3.4))); std::cout << expr->show() << "=" << expr->eval() << '\n'; std::cout << "~~~~ leave " << __func__ << "() ~~~~\n"; }
void test_CompactNode() { std::cout << "\n~~~~ enter " << __func__ << "() ~~~~\n"; using Node = s4prc::CompactNode; std::vector<Node> nodes; auto expr=make(nodes, '*', make(nodes, 10.1), make(nodes, '+', make(nodes, 5.2), make(nodes, 3.4))); std::cout << nodes[expr].show(nodes) << "=" << nodes[expr].eval(nodes) << '\n'; std::cout << "~~~~ leave " << __func__ << "() ~~~~\n"; }
void test_perf() { std::cout << "\n~~~~ " << __func__ << "() ~~~~\n"; #if !defined NDEBUG std::cout << "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" " Built for debug purpose at first " "(measurements will not be relevant).\n" " For an actual experiment, rebuild with: " " make rebuild opt=1\n" "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~" "~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"; #endif const auto perf_build= [&](const auto &value_fnct, const auto &oper_fnct) { const int node_count=1000; auto expr=value_fnct(1.0); for(int i=0; i<node_count; ++i) { expr=oper_fnct('+', value_fnct(0.1), expr); } return expr; }; const auto perf_run= [&](const auto &name, auto &expr, const auto &eval_fnct) { const int iter_count=100'000; for(int i=0; i<10; ++i) { eval_fnct(expr); // warm-up before measure } const double t0=get_time(); double result=0.0; for(int i=0; i<iter_count; ++i) { result+=eval_fnct(expr); } const double dt=get_time()-t0; std::cout << name << " takes " << dt << " s\n"; std::cout << ">>> result=" << result << '\n'; }; if(1) { using Node = s4prc::RawNode; auto expr=perf_build( [&](const auto &value) { // WARNING: never use new in real C++ code! return new Node{value}; }, [&](const auto &oper, const auto &left, const auto &right) { // WARNING: never use new in real C++ code! return new Node{oper, left, right}; }); perf_run("RawNode", expr, [&](const auto &expr) { return expr->eval(); }); // WARNING: never use delete in real C++ code! delete expr; // mandatory to prevent memory-leak! } if(1) { using Node = s4prc::SmartNode; auto expr=perf_build( [&](const auto &value) { return std::make_unique<Node>(value); }, [&](const auto &oper, auto &&left, auto &&right) { return std::make_unique<Node>(oper, std::move(left), std::move(right)); }); perf_run("SmartNode", expr, [&](const auto &expr) { return expr->eval(); }); } if(1) { using Node = s4prc::CompactNode; std::vector<Node> nodes; auto expr=perf_build( [&](const auto &value) { return make(nodes, value); }, [&](const auto &oper, const auto &left, const auto &right) { return make(nodes, oper, left, right); }); perf_run("CompactNode", expr, [&](const auto &expr) { return nodes[expr].eval(nodes); }); } }
int main() { test_alloc(); test_RawNode(); test_SmartNode(); test_CompactNode(); test_perf(); return 0; }
//----------------------------------------------------------------------------