C扩展 C++回顾到入门

时间:2021-08-06 06:44:36

引言

  C扩展也称C++, 是一个复(za)杂(ji)优(ken)秀(die)的语言. 本文通过开发中常用C++方式
来了解和回顾C++这么语言. C++看了较多的书但还是觉得什么都不会. 只能说自己还付出太少,哎.

在引言部分我们先感受C++类的设计.

有个如下需求, 设计一个简单的日志系统. 先看下面 LogSimple.hpp

#ifndef _HPP_LOGSIMPLE
#define _HPP_LOGSIMPLE #include <iostream> using namespace std; // 特殊技巧构建类构造器
class LogInit {
// 设置LogSimple为友员, 可以访问当前私有属性
friend class LogSimple; // _log写普通文件, _wf写级别高的文件
static FILE* _log;
static FILE* _wf; // 私有的构造器, 证明这个类是私有类
LogInit() {
const char* log = "rpc.log";
const char* wf = "rpc.log.wf"; _log = fopen(log, "ab");
if (NULL == _log) {
fprintf(stderr, "fopen is error : %s\n", log);
exit(EXIT_FAILURE);
} _wf = fopen(wf, "ab");
if (NULL == _wf) {
fclose(_log);
fprintf(stderr, "fopen is error : %s\n", wf);
exit(EXIT_FAILURE);
}
} // 析构打开的句柄
~LogInit() {
fclose(_wf);
fclose(_log);
}
}; // 定义静态变量
FILE* LogInit::_log = NULL;
FILE* LogInit::_wf = NULL; // 基础的日志系统
class LogSimple { protected:
// 只能在当前类和继承类中使用的单例对象, 这个只是声明
static LogInit _li; protected:
// 打印普通信息
void LogWrite(string msg) {
fprintf(LogSimple::_li._log, msg.c_str());
} // 打印等级高信息
void WfWrite(string msg) {
fprintf(LogSimple::_li._wf, msg.c_str());
} public:
virtual void Log(string msg) = ;
}; // 定义在 LogSimple 中声明的静态量
LogInit LogSimple::_li; // Debug 模式日志
class LogDebug : public LogSimple { public:
// 重写Log输出内容
void Log(string msg) {
#if defined(_DEBUG)
this->LogWrite(msg);
#endif
}
}; // Debug 模式日志
class LogFatal : public LogSimple { public:
// 重写Log输出内容
void Log(string msg) {
this->LogWrite(msg);
this->WfWrite(msg);
}
}; #endif // !_HPP_LOGSIMPLE

这里使用了 *.hpp 文件,也称C++的充血模型. 当使用 hpp头文件时候表示当前代码是开源的, 头文件和实现都在一起.

并且不使用全局变量和全局函数.

还有这段代码

    // 设置LogSimple为友员, 可以访问当前私有属性
friend class LogSimple; ...... // 只能在当前类和继承类中使用的单例对象, 这个只是声明
static LogInit _li;

是构建上层语言的 类的构造器. "只会在第一次使用这个类的时候构建这个对象". C++中通过技巧能够完成一切, 是一个强调技巧,强混乱约束的语言.

测试代码如下 main.cpp

#include "LogSimple.hpp"

/*
* 主函数, 测试简单的日志系统
* 快速熟悉C++类的使用方法.
*/
int main(void) { LogSimple *log;
LogDebug debug;
LogFatal fatal; // 简单测试
log = &debug;
log->Log("debug 日志测试!\n"); log = &fatal;
log->Log("fatal 日志测试\n"); // 测试完毕
puts("测试完毕!"); system("pause");
return ;
}

运行结果

C扩展 C++回顾到入门

生成日志文件图

C扩展 C++回顾到入门

再扯一点, C++类中静态变量, 分两步构造,先在类中声明, 再在外面定义分配实际空间. 好,这里关于C++的类回顾完毕.

前言

  前言部分回顾一下C++中模板用法.

开始先回顾了解函数模板, 看下面测试文件 main.cpp

#include <iostream>

using namespace std;

/*
* 快速排序递归核心, 当前是从小到大排序
*/
template <typename T> static void
_sortquick(T a[], int si, int ei) {
// 递归结束条件
if (si >= ei) return; int low = si, high = ei;
T axle = a[low];
while (low < high) {
// 找最右边不合适点
while (low < high && a[high] > axle)
--high;
if (low >= high) break;
a[low++] = a[high]; //找最左边不合适点
while (low < high && a[low] < axle)
++low;
if (low >= high) break;
a[high--] = a[low];
}
// 分界点找好了, 归位 此时low == high
a[low] = axle; //新一轮递归
_sortquick(a, si, low - );
_sortquick(a, high + , ei);
} // 包装对外使用的快排接口
template<typename T> inline void
sortquick(T a[], int len) {
_sortquick(a, , len - );
} /*
* 这里温故函数模板,以快速排序为例
*/
int main(void) {
// 开始测试, 模板函数
int a[] = {, , , , , , , , , , }; // 开始调用测试 是 sortquick<int> 自动推导
sortquick(a, sizeof(a) / sizeof(*a)); puts("排序后数据为:");
for (int i = ; i < sizeof(a) / sizeof(*a); ++i)
printf("%d ", a[i]);
putchar('\n'); system("pause");
return ;
}

通过 template<typename T> 构建一个模板的快排函数. 测试结果如下

C扩展 C++回顾到入门

再来回顾一下 模板类用法 我们构建一个 简单的 智能指针类 AutoPtr.hpp

#ifndef _HPP_AUTOPTR
#define _HPP_AUTOPTR #include <cstring>
#include <cstdlib> /**
*简单的智能指针,支持创建基本类型 基本类型数组
*支持智能管理对象类型,对象数组类型
*不允许赋值构造,复制构造,不允许new创建
*/
template<typename T> class AutoPtr {
T *_ptr;
unsigned _len;
AutoPtr<T>(const AutoPtr<T> &autoPtr);
AutoPtr<T> &operator=(const AutoPtr<T> &autoPtr);
void *operator new(unsigned s); public:
AutoPtr(unsigned len = 1U)
{
this->_len = len;
this->_ptr = !len ? NULL : (T*)calloc(len, sizeof(T));
}
~AutoPtr(void)
{
for (unsigned u = this->_len; u > 0U; --u)
this->_ptr[u - ].~T();//delete的本质
free(this->_ptr);
} inline T& operator*(void) const
{
return *this->_ptr;
} inline T* operator->(void) const
{
return this->_ptr;
} inline T& operator[](unsigned idx) const
{
return this->_ptr[idx];
} inline T* operator+(unsigned idx) const
{
return this->_ptr + idx;
}
//获取智能托管资源的长度,在数组中有用
inline unsigned size(void)
{
return this->_len;
}
}; #endif // !_HPP_AUTOPTR

测试代码如下 main.cpp

#include <iostream>
#include "AutoPtr.hpp" using namespace std; struct abx {
int a;
float b;
char *c;
}; /*
* 这里将处理 泛型类的使用讲解
* 泛型还是在开发中少用.这里只是初级熟悉篇.
*/
int main(void) { // 先使用基础的用法
AutoPtr<int> iptr; *iptr = ;
printf("*iptr = %d\n", *iptr); // 使用 数组类型
AutoPtr<abx> abs();
printf("abs[6].c = %s\n", abs[].c); system("pause");
return ;
}

演示结果

C扩展 C++回顾到入门

通过上面两个例子, 练习一下基本熟悉泛型语法简易用法了.高级的用法, 那还得春夏秋冬......

正文

  这里简单讲解STL中开发中用到的容器类.使用一些简单例子,方便上手使用.

先看list 链表使用案子

同样通过代码开始 main.cpp, 通过list处理随机业务.

#include <iostream>
#include <cassert>
#include <ctime>
#include <list> using namespace std; /*
* 主函数 - 熟悉STL list 用法
* 业务需求如下:
* 有一堆这样数据
* 标识 权重
* 1 100
* 2 200
* 3 100
* ... ...
* 需要随机出一个数据.
*/ class RandGoods {
list<int> idxs; //存所有索引的
list<int> weights; //存所有权重的
int sum; //计算总的权重和
public:
RandGoods(void) {
this->sum = ;
// 初始化随机种子
srand((unsigned)time(NULL));
} /*
* 添加数据
*/
void Add(int idx, int weidth) {
// 简单检测一下参数
assert(idx>= && weidth > ); this->idxs.push_front(idx);
this->weights.push_front(weidth);
this->sum += weidth;
} // 得到一个随机数据
int Get(void) {
int ns = ;
int rd = rand() % sum;
int len = this->weights.size();
list<int>::iterator it = this->idxs.begin();
list<int>::iterator wt = this->weights.begin(); while (wt != this->weights.end()) {
ns += *wt;
if (ns > rd)
return *it;
++it;
++wt;
} return -;
} // 输出所有数据
void Print(void) {
list<int>::iterator it = this->idxs.begin();
list<int>::iterator wt = this->weights.begin(); puts("当前测试数据如下:");
while (wt != this->weights.end()) {
printf("%3d %3d\n", *it, *wt);
++it;
++wt;
}
}
}; /*
* 温故 list用法, C++ STL 没有上层语言封装的好用
*/
int main(void) {
// 随机对象
RandGoods rg;
int len = rand() % + ; // 返回是 [5, 24] //添加数据
for (int i = ; i < len; ++i) {
int weight = rand() % + ;
rg.Add(i, weight);
} // 这里测试 得到数据
rg.Print(); // 得到一个数据
int idx = rg.Get(); printf("得到随机物品索引:%d\n", idx); system("pause");
return ;
}

对于STL 库有很多功能, 这里就是最简单的使用方式. 工作中需要用到高级的用法, 可以及时查. 关键是有思路.

演示结果

C扩展 C++回顾到入门

C++ 的list 没有 java和C#的List好用. 差距太大. 或者说STL相比上层语言提供的容器, 显得不那么自然. 估计是C++是开创者,

后面的语言知道坑在那, 简化创新了. 也可以用vector可变数组代替list. 如果在C中直接用语法层提供的可变数组 int max = 10; int a[max];

在栈上声明可变数组就可以了.

再看queue 队列使用方式

关于stl 容器用法都是比较基础例子, 重点能用, 高级的需要看专门介绍的书籍. 关于队列底层库中常用. 和多线程一起配合.

流程很绕, 这里简单写个容易的例子如下main.cpp

#include <iostream>
#include <queue> using namespace std; /*
* 这里使用 queue队列, 简单使用了解
* 最简单的生产后, 直接消耗
*/
int main(void) { queue<double> qds;
int i, len = rand() % + ;
double c;
int a, b; puts("生产的数据如下:");
// 先生产 队列是尾巴插, 头出来
for (i = ; i < len; ++i) {
a = rand();
b = rand();
if (a >= b)
c = a + 1.0 * b / a;
else
c = (double)-b - 1.0 * a / b; // 队列中添加数据
printf("%f ", c);
qds.push(c);
} puts("\n释放的数据如下:");
while (!qds.empty()) {
c = qds.front();
printf("%f ", c); qds.pop();
}
putchar('\n'); system("pause");
return ;
}

运行截图如下

C扩展 C++回顾到入门

注意的是C++队列是尾查头出.

后看map 键值对使用例子

先看 main.cpp

#include <iostream>
#include <map>
#include <string> using namespace std; /*
* 这里是使用 map. 简单的熟悉map的使用方法
*/
int main(void) {
map<string, string> kvs;
const char* strs[] = { "Sweet", "are", "the", "uses", "of", "adversity",
"Knowledge", "is", "one", "thing", "but", "faith", "is", "another" }; // 先添加数据
int i;
pair<map<string, string>::iterator, bool> pit;
for (i = ; i < sizeof(strs) / sizeof(*strs); ++i) {
pit = kvs.insert(pair<string, string>(strs[i - ], strs[i]));
if (!pit.second) {
printf("插入失败<%s,%s>\n", strs[i-], strs[i]);
}
} // 这里开始查找处理
map<string, string>::iterator it = kvs.find("are");
if (it != kvs.end())
printf("找见了 %s => %s\n", it->first.c_str(), it->second.c_str());
else
printf("没有找见 are => NULL\n"); // 全局输出
puts("当前的数据内容如下:");
for (it = kvs.begin(); it != kvs.end(); ++it) {
printf("%s => %s\n", it->first.c_str(), it->second.c_str());
} system("pause");
return ;
}

运行结果

C扩展 C++回顾到入门

到这里基本上C++ 语言中常用的语法规则, 基本都回顾熟悉完毕了. 后面随着开发, 慢慢了解突破. 最快的熟悉手段还是大量看专业书籍和敲代码.

后记

  错误是难免, 这里纯属回顾C++基础语法. 有问题随时交流, 接受任何C++高玩的批评. 拜~~