C + + programming and object model design

C + + programming and object model design

Conversion function

#include <iostream>

class Fraction
{
public:
  Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
  operator double() const {
    return ((double)m_numerator/m_denominator);
  }
private:
  int m_numerator;   // molecule
  int m_denominator; // denominator
};

int main() {
  Fraction f(3, 5);
  double d = 4+f;
  std::cout << d << std::endl;
}

Non explicit one argument constructor

Automatically convert other types to the current type

#include <iostream>

class Fraction
{
public:
  Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
  //operator double() const {
  //  return ((double)m_numerator/m_denominator);
  //}
  Fraction operator +(const Fraction& f) const {
    return ((double)m_numerator/m_denominator);
  }
private:
  int m_numerator;   // molecule
  int m_denominator; // denominator
};

int main() {
  Fraction f(3, 5);
  Fraction d = f+4;
}

If you add

#include <iostream>

class Fraction
{
public:
  explicit Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
  Fraction operator +(const Fraction& f) const {
    return ((double)m_numerator/m_denominator);
  }
private:
  int m_numerator;   // molecule
  int m_denominator; // denominator
};

int main() {
  Fraction f(3, 5);
  Fraction d = f+4;
}

Will report an error

non-expllicit.cpp: In member function'Fraction Fraction::operator+(const Fraction&) const'in:
non-expllicit.cpp:8:32: Error: could not convert '((double)(int)((const Fraction*)this)->Fraction::m_numerator / (double)(int)((const Fraction*)this)->Fraction::m_denominator)' from 'double' to 'Fraction'
    8 |     return ((double)m_numerator/m_denominator);
      |            ~~~~~~~~~~~~~~~~~~~~^~~~~~~~~~~~~~~
      |                                |
      |                                double

pointer-like classes

Smart pointer

template<calass T>
class shared_ptr
{
public:
  T& operator*() const {
    return *px;
  }
  T* operator->() const {
    return px;
  }
  share_ptr(T* p) : px(p) {}
  
private:
  T*    px;
  long* pn;
}

iterator

reference operator*() const {
  return (*node).data;
}

pointer operator->() const {
  return &(operator*());
}

function-like classes

Overload ()

namespace

#include <iostream>

namespace fraction {
  class Fraction {
  public:
    Fraction(int num, int den = 1) : m_numerator(num), m_denominator(den) {}
    operator double() const { return ((double)m_numerator / m_denominator); }

  private:
    int m_numerator;   // molecule
    int m_denominator; // denominator
  };
}

int main(int argc, char *argv[]) {

  fraction::Fraction f(3, 5);
  double d = 4+f;
  std::cout << d << std::endl;

  return 0;
}

Member template

namespace pair_tmpl {
  template<class T1, class T2>
  struct pair{
    typedef T1 first_type;
    typedef T2 second_type;

    T1 first;
    T2 second;

    pair() : first(T1()), second(T2()) {}
    pair(const T1& a, const T2& b) : first(a), second(b) {}

    // Member template
    template <class U1, class U2>
    pair(const pair<U1, U2>& p)
      : first(p.first), second(p.second) {}
  };

  class Base1 {};
  class Sub1 : public Base1 {};

  class Base2 {};
  class Sub2 : public Base2 {};
}

int main(int argc, char *argv[]) {

  pair_tmpl::pair<pair_tmpl::Sub1, pair_tmpl::Sub2> p;
  pair_tmpl::pair<pair_tmpl::Base1, pair_tmpl::Base2> p2(p);

  return 0;
}

template<typename _Tp>
class shared_ptr : public __shared_ptr<_TP>
{
...
  template<typename _Tp1>
  explicit shared_ptr(_Tp1* __p): __shared_ptr<_Tp>(__p) {}
...
}

Base1 * ptr = new Sub1;

shared_ptr<Base1> sptr(new Sub1);

Specialization template specialization

In fact, templates are generalization

On the basis of generalization, different treatments are made according to different types

namespace specialization {
template <class Key> struct hash {};

template <> struct hash<char> {
  std::size_t operator()(char x) const { return x; }
};

template <> struct hash<int> {
  std::size_t operator()(char x) const { return x; }
};

template <> struct hash<long> {
  std::size_t operator()(char x) const { return x; }
};

} // namespace specialization


partial template specialization

  • Partial specialization of number
template<typename T, typename Alloc=...>
class vector
{
  ...
};

template<typename Alloc=...>
class vector<bool, Alloc>
{
  ...
};
  • Partial specialization of range
template <typename T> class C {};

template <typename T> class C<T *> {};

C<string> obj1;
C<string*> obj2;

Template parameters


template <typename T, template <typename> class SmartPtr> class XCls {
private:
  SmartPtr<T> sp;

public:
  XCls() : sp(new T) {}
};

Three themes

Variable number of template parameters

namespace variadic {
  void print() {} // The last thing I want to do when I call a parameter.

  template <typename T, typename... Types>
  void print(const T& firstArg, const Types&... args) {
    std::cout << firstArg << std::endl;
    print(args...);
  }
}
  • sizeof...(args)
namespace variadic {
  void print() {}

  template <typename T, typename... Types>
  void print(const T& firstArg, const Types&... args) {
    printf("%d\n", sizeof...(args));
    std::cout << firstArg << std::endl;
    print(args...);
  }
}

auto

list<string> c;
...
list<string>::iterator ite;
ite = find(c.begin(), c.end(), target);

auto ite = find(c.begin(), c.end()m target);

ranged-base for

for (int i : {2, 3, 4, 5, 6, 7, 8, 9}) {
  cout << i << endl;
}

vector<double> vec;

for (auto elem : vec) {
  cout << elem << endl;
}

for (auto& elem : vec) {
  elem *= 3;
}

Reference (important)


namespace reference {
  typedef struct Stag {int a, b, c, d;} S;
}

int main(int argc, char *argv[]) {

  double x = 0;
  double* p = &x;
  double& r = x;

  std::cout << sizeof(x) << std::endl;
  std::cout << sizeof(p) << std::endl;
  std::cout << sizeof(r) << std::endl;

  std::cout << x << std::endl;
  std::cout << &x << std::endl;
  std::cout << p << std::endl;
  std::cout << *p << std::endl;
  std::cout << &p << std::endl;
  std::cout << r << std::endl;
  std::cout << &r << std::endl;

  reference::S s;
  reference::S& rs = s;

  std::cout << sizeof(s) << std::endl;
  std::cout << sizeof(rs) << std::endl;
  std::cout << &s << std::endl;
  std::cout << &rs << std::endl;

  return 0;
}

8
8
8

0
0x7fffd7f79b10
0x7fffd7f79b10
0
0x7fffd7f79b08
0
0x7fffd7f79b10

16
16

0x7fffd7f79af8
0x7fffd7f79af8

There is a good saying that reference is a beautiful pointer

Purpose of reference

  • Note that const is part of the function signature

Composition & Inheritance & Construction and Deconstruction in delegation

combination

  • Structure from inside to outside
Container::Container() : Component() {}
  • Deconstruction from outside to inside
Container::~Container() { ~Component(); }

Note that the compiler can only help us call the default constructor. If necessary, we need to implement it ourselves

inherit

Construction and deconstruction under inheritance + combination relationship

  • Structure from inside to outside
    The constructor of the subclass calls the default constructor of the parent class first, then calls the default constructor of the component, and finally executes itself.
Sub::Sub() : Base(), Component() {}
  • Deconstruction from outside to inside
    The destructor of the subclass first destruct itself, invokes the destructor of the component, and finally calls the constructor of the parent class.
Sub::~Sub() { 
  // Do something;
  ~Component();
  ~Base();
}

About vptr and vtbl

namespace Virtual {
  class A{
  public:
    virtual void vfunc1();
    virtual void vfunc2();
    void func1();
    void func2();

  private:
    int m_data1, m_data2;
  };

  class B : public A{
  public:
    virtual void vfunc1();
    void func2();

  private:
    int m_data3;
  };

  class C : public B{
  public:
    virtual void vfunc1();
    void func2();
  private:
    int m_data_4;
  };
}

(*(p->vptr)[n])(p)

(* p->vptr[n])(p)

Static binding

call ox.....

Dynamic binding

  1. Pointer call
  2. Upward transformation
  3. A virtual function is called
(*(p->vptr)[n])(p)

About this

this is the address of the current object

Look at an example. This is the same as the virtual mechanism above

class CDocument {
public:
  virtual Serialize();
};

// An application framework
CDocument::OnFileOpen() {
  ...
  Serialize(); // A virtual function
  ...
}

// application program
class CMyDoc: public CDocument {
  virtual Serialize() {
    ...
  }
}

int main() {
  CMyDoc myDoc;
  ...
  myDoc.OnFileOpen(); // Call parent method
  // Equivalent to cdocument:: onfileopen (& MyDoc); this == &myDoc
  // Because it is inheritance, there is actually a parent class (calling the constructor) in the child class 
  // The pointer passed in cdocument:: onfileopen (& MyDoc) is a parent class that transforms upward (child - > parent)
  // Then, when it comes to Serialize(), because it is a member function, it is equivalent to this - > Serialize() = = & MyDoc - > Serialize()
  // Then it is equivalent to (* (this - > VPTR) [n]) (this), which is dynamic binding
  
}


About dynamic binding (see assembly)

B b;

A a = (A)b;
a.vfunc1(); // This sentence is a static binding

A* pa = new B;
ps->vfunc1();

pa = &b;
pa->vfunc1();

Talk about const

const object(data members cannot be changed)Non const object (data members) can be changed
Const member functions (ensure that data members are not modified)TT
Non const member functions (data members are not guaranteed to be modified)FT

When const and non const versions of member functions exist at the same time, const object can only call const version, and non const object can only call non const version

Design in std::String

When const and non const versions of member functions exist at the same time, const object can only call const version, and non const object can only call non const version

class template std::basic_string<...>There are the following two member functions:

charT operator[] (size_typr pos) const {
  // Copy on write is not considered
}

reference operator[] (size_type pos) {
  // Copy on write must be considered
}

About New Delete

No virtual function

#include <iostream>
#include <stddef.h>
#include <stdlib.h>
#include <string>

class Foo {
public:
  int _id;
  long _data;
  std::string _str;

public:
  Foo() : _id(0) {
    std::cout << "call default ctor.this=" << this << "id=" << _id << std::endl;
  }
  Foo(int i) : _id(i) {
    std::cout << "call default ctor.this=" << this << "id=" << _id << std::endl;
  }
  //Virtual ~ foo() {STD:: cout < < call dTOR. This = "< < this < < id =" < < u id < < STD:: endl;}
  ~Foo() { std::cout << "call dtor.this=" << this << "id=" << _id << std::endl; }

  static void *operator new(size_t size);
  static void operator delete(void *pdead, size_t size);
  static void *operator new[](size_t size);
  static void operator delete[](void *pdead, size_t size);
};

void *Foo::operator new(size_t size) {
  std::cout << "call new() size=" << size <<std::endl;
  std::cout << "Apply for memory first\n";
  Foo *p = (Foo *)malloc(size);
  return p;
}

void Foo::operator delete(void *pdead, size_t size) {
  std::cout << "call delete()\n";
  std::cout << "free Memory\n";
  free(pdead);
}

void *Foo::operator new[](size_t size) {
  std::cout << "call new[]() size=" << size <<std::endl;
  std::cout << "Apply for memory first\n";
  Foo *p = (Foo *)malloc(size);
  return p;
}

void Foo::operator delete[](void *pdead, size_t size) {
  std::cout << "call delete[]()\n";
  std::cout << "free Memory\n";
  free(pdead);
}

int main(int argc, char *argv[]) {

  std::cout << sizeof(Foo) << std::endl;

  Foo *p = new Foo(7);
  delete p;

  Foo *pArray = new Foo[5];
  delete[] pArray;

  return 0;
}

result

48
 call new() size=48
 Apply for memory first
 call default ctor.this=0x55ee164252c0id=7
 call dtor.this=0x55ee164252c0id=7
 call delete()
free Memory
 call new[]() size=248
 Apply for memory first
 call default ctor.this=0x55ee16425308id=0
 call default ctor.this=0x55ee16425338id=0
 call default ctor.this=0x55ee16425368id=0
 call default ctor.this=0x55ee16425398id=0
 call default ctor.this=0x55ee164253c8id=0
 call dtor.this=0x55ee164253c8id=0
 call dtor.this=0x55ee16425398id=0
 call dtor.this=0x55ee16425368id=0
 call dtor.this=0x55ee16425338id=0
 call dtor.this=0x55ee16425308id=0
 call delete[]()
free Memory

Virtual function

#include <iostream>
#include <stddef.h>
#include <stdlib.h>
#include <string>

class foo {
public:
  int _id;
  long _data;
  std::string _str;

public:
  foo() : _id(0) {
    std::cout << "call default ctor.this=" << this << "id=" << _id << std::endl;
  }
  foo(int i) : _id(i) {
    std::cout << "call default ctor.this=" << this << "id=" << _id << std::endl;
  }
  virtual ~foo() { std::cout << "call dtor.this=" << this << "id=" << _id << std::endl; }
  //~Foo() {STD:: cout < < call dTOR. This = "< < this < < id =" < < u id < < STD:: endl;}

  static void *operator new(size_t size);
  static void operator delete(void *pdead, size_t size);
  static void *operator new[](size_t size);
  static void operator delete[](void *pdead, size_t size);
};

void *foo::operator new(size_t size) {
  std::cout << "call new() size=" << size <<std::endl;
  std::cout << "Apply for memory first\n";
  foo *p = (foo *)malloc(size);
  return p;
}

void foo::operator delete(void *pdead, size_t size) {
  std::cout << "call delete()\n";
  std::cout << "free Memory\n";
  free(pdead);
}

void *foo::operator new[](size_t size) {
  std::cout << "call new[]() size=" << size <<std::endl;
  std::cout << "Apply for memory first\n";
  foo *p = (foo *)malloc(size);
  return p;
}

void foo::operator delete[](void *pdead, size_t size) {
  std::cout << "call delete[]()\n";
  std::cout << "free Memory\n";
  free(pdead);
}

int main(int argc, char *argv[]) {

  std::cout << sizeof(foo) << std::endl;

  foo *p = new foo(7);
  delete p;

  foo *parray = new foo[5];
  delete[] parray;

  return 0;
}

result

56
 call new() size=56
 Apply for memory first
 call default ctor.this=0x55d683a2f2c0id=7
 call dtor.this=0x55d683a2f2c0id=7
 call delete()
free Memory
 call new[]() size=288
 Apply for memory first
 call default ctor.this=0x55d683a2f308id=0
 call default ctor.this=0x55d683a2f340id=0
 call default ctor.this=0x55d683a2f378id=0
 call default ctor.this=0x55d683a2f3b0id=0
 call default ctor.this=0x55d683a2f3e8id=0
 call dtor.this=0x55d683a2f3e8id=0
 call dtor.this=0x55d683a2f3b0id=0
 call dtor.this=0x55d683a2f378id=0
 call dtor.this=0x55d683a2f340id=0
 call dtor.this=0x55d683a2f308id=0
 call delete[]()
free Memory

Keywords: C++ Back-end

Added by whitelion on Wed, 10 Nov 2021 08:32:49 +0200