奇淫巧技

shane1911 / 2023-06-23 / 原文

编程之道的哲学原理

1.综述

​ 世界上最傻的事就是重复做无意义的事情——如果你认可这个论断的话,那么本文或将对你有所裨益。编程是赋予机器运作规律的一门语言,你学外语可能是为了学习国外知识,当然也可能为了学习诸如姿势此类知识。而编程语言当然是为了PUA你的电脑为你完成某些工作,但可能某些繁复琐碎的PUA让你感到厌烦,你不得不重复为实现某些相似的工作而编写一些似乎曾经做过的类似工作,这种重新做一遍的活,你似乎颇为厌烦,至少笔者如此认为。
​ 因此,我们这里隆重的讨论面对对象(OOP)与泛型编程的使用——设计可以重复使用的代码,避免重复设计轮子。这里简略记录一些笔者的经验。
​ OOP是一种常用的避免重复发明轮子的手法,且随着C++技术的进步已斟化境,关于继承的相关介绍也是浩如烟海,然c++泛型编程却常忽被视,泛型编程主要以template形式展现,关于template的许多高阶技巧,初学前望之俨然,了解之后即之也温,钻研之后其言也厉。一言蔽之,OOP针对方法的泛化,泛型编程针对数据类型的泛化。

2.继承

​ 网上关于继承的介绍不知繁几,这里出于鄙人的兴趣,寥寥几笔过之。
​ 先定义一个基类,构造函数,可显示构造亦可使用默认构造函数(复制构造函数,赋值构造函数...),析构函数须定义为虚函数,以保证派生类只有一个析构函数。

#pragma once
#include <iostream>
#include <vector>
using namespace std;

class base {
 protected:
  double a;
  vector<int> nums;

 public:
  base() = delete;
  base(const double& n) : a(n) {}
  // base& operator=(base&& orther) {
  //   a = orther.a;
  //   orther.a = 0;
  //   nums = std::move(orther.nums);
  //   return *this;
  // }
  //   base(const base& orther) {
  //     a = orther.a;
  //     nums = orther.nums;
  //   }
  virtual ~base() {}
  inline double Geta() { return a; }
  // inline vector<int>* Getnums() { return &nums; }
};

​ 定义派生类:可使用using语句使用基类构造函数和私有数据成员或函数,如定义显示构造函数须初始化基类构造函数。


#include "base.h"
class secone : public base {
  using base::a;
  using base::base;
  using base::nums;
 public:
  secone() = delete;
  secone &operator=(const secone &) = delete;
  // secone(const double& n) : base(n) {}
  // secone(const secone &) = delete;
  secone &operator=(secone &&orther) {
    // if (this == &orther) return *this;
    a = orther.a;
    orther.a = 0;
    nums = std::move(orther.nums);
    // cout << "b = " << orther.a << endl;
    return *this;
  }
  secone(secone &&orther) : base(orther) {
    a = orther.a;
    nums = orther.nums;
    orther.a = 0;
    // cout << "b = " << orther.a << endl;
  }
  ~secone();

  inline double Geta() { return a; }

  inline void Seta(const double &n) { a = n; }
};
secone::~secone() {}

​ 至此,关于继承的相关用法(such as: 多重继承,抽象基类...)不做过多叙述。

3.泛型编程

​ 泛型编程主要以模板形式表现出来,可以有模板函数,模板类。

​ 一个模板类的定义如下:该模板包含一个模板参数和一个非模板参数,另外补充一点返回常数的函数需要constexpr修饰。

/*
 * @Date: 2023-06-04 11:10:19
 * @LastEditors: shane1911
 * @FilePath: /C++_Professional/type/test_main.cc
 * @Description: 料知此生无大事,关心雪后有梅花。
 */
#include <iostream>
#include <string>
using namespace std;

template <class T, int n>
class mydata {
 private:
  T a;
  double h;

 public:
  mydata(const T& one);
  virtual ~mydata() = default;
  inline T Get() { return a; }
  inline double Geth(const double& b) {
    h = b * n;
    return h;
  }
};

template <class T, int n>
mydata<T, n>::mydata(const T& one) {
  a = one;
}

int main() {
  mydata<string, 5> test("ok");
  cout << " T: " << test.Get() << endl;
  cout << " h: " << test.Geth(0.6) << endl;
}

​ 除了上述模板参数,模板也可以传入tempate template参数

#include <iostream>
#include <optional>
#include <string>
#include <vector>
using namespace std;

// template <class T, int n>
template <typename T, int n,
          template <typename E, typename Allocator = allocator<E>>
          class container = vector>
class mydata {
 private:
  T a;
  double h;
  container<T> mycell_;

 public:
  mydata(const T& one);
  virtual ~mydata() = default;
  inline T Get() { return a; }
  inline double Geth(const double& b) {
    h = b * n;
    return h;
  }
  void UsingVector(const T& num);
};

template <typename T, int n,
          template <typename E, typename Allocator = allocator<E>>
          class container>
mydata<T, n, container>::mydata(const T& one) {
  a = one;
}

template <typename T, int n,
          template <typename E, typename Allocator = allocator<E>>
          class container>
void mydata<T, n, container>::UsingVector(const T& num) {
  for (int i = 0; i < num; i++) {
    mycell_.push_back(i);
  }
  for (auto& p : mycell_) {
    cout << p << endl;
  }
}

4.单例模式

关于上述技术的综合使用,下面借用一个设计模式:单例模式
首先定义一个单例的基类:

/*
 * @Date: 2023-05-19 09:47:46
 * @LastEditors: shane1911
 * @LastEditTime: 2023-06-04 19:00:47
 * @FilePath: /C++_Professional/singleinstance.h
 * @Description: Start all over again !!!
 */
#pragma once
#include <memory>
#include <string>
namespace utils {
template <typename T>
class Singleton {
 private:
  Singleton<T>(const Singleton<T>&) = delete;
  Singleton<T>& operator=(const Singleton<T>&) = delete;

  Singleton<T>() = default;

  static std::unique_ptr<T> m_instance;
  static std::once_flag m_once;

 public:
  virtual ~Singleton<T>() = default;

  static T* getInstance() {
    std::call_once(m_once, []() { m_instance.reset(new T); });
    return m_instance.get();
  }

  template <typename... Args>
  static T* getInstance2nd(Args&&... args) {
    std::call_once(m_once, [&]() {
      m_instance.reset(new T(std::forward<Args>(args)...));
    });
    return m_instance.get();
  }
};

// 初始化静态变量
template <typename T>
std::unique_ptr<T> Singleton<T>::m_instance;
template <typename T>
std::once_flag Singleton<T>::m_once;
}  // namespace utils

此后所有的消息类都可继承至该模板类函数:

/*
 * @Date: 2023-05-18 17:16:54
 * @LastEditors: shane1911
 * @LastEditTime: 2023-05-28 21:03:16
 * @FilePath: /c++_multi_thread/test_multi_thead.cpp
 * @Description: Start all over again !!!
 */
#include <iostream>
#include <memory>
#include <mutex>
#include <string>
#include <vector>

#include "singleinstance.h"
#include "thread"
using namespace std;

class MyClass {
 public:
  MyClass(){};

  virtual ~MyClass(){};

  void fun(const string& s) {
    cout << "this is fun, input: " << s << endl;
    m_strData = s;
  }
  string get() { return m_strData; }

 private:
  string m_strData;
};

void Run1() {
  int index = 0;
  while (1) {
    auto instMyClass1 = utils::Singleton<MyClass>::getInstance();
    instMyClass1->fun("write" + to_string(index++));
    std::this_thread::sleep_for(std::chrono::milliseconds(100));
  }
}

void Run2() {
  while (1) {
    auto instMyClass2 = utils::Singleton<MyClass>::getInstance();
    cout << "in run2: " << instMyClass2->get() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(200));
  }
}

int main() {
  int i = 21;
  thread t(Run1);
  t.detach();
  thread t2(Run2);
  t2.detach();
  std::this_thread::sleep_for(std::chrono::milliseconds(1000));
}