// CPPTEST.cpp : 定义控制台应用程序的入口点。
// #include "stdafx.h"
#include<iostream>
#include <map>
#include<fstream>
#include<cassert>
#include <sstream>
#include"TMyNumOperator.h"
#include"abc.h"
#include <list>
#include<thread>
#include <vector>
#include <algorithm>
using namespace std;
using std::cin;
using std::cout; //using namespace std;
//
//class CBase{
//protected://注意,可以使用C#风格的定义时初始化
// std::string name = "NoOne";
// int age = -20;
// int sex = 1;
//public:
// float *pData = new float[20]{1, 2, 3, 4, 5};
//public:
// virtual ~CBase(){//虚析构函数,防止内存泄漏:对基类指针调用delete时,会从子类一直析构到基类
// cout << "~cbase" << endl;
// }
//};
//
////基类的私有成员不会被继承,这和C#完全一样
//class CStudent : public CBase{
//public:
// std::map<int, std::string> _projs;
// CStudent(){
// pData = new float[20]{1, 2, 3, 4, 5};
// }
//public:
// void SetName(const std::string& name){
// CBase::name = name;//如果CBase.name定义为私有,这里就不可访问
// this->name = name; //等价于上一行
// }
//
// const string& GetName(){
// return this->name;
// }
//
// ~CStudent(){//若采用浅拷贝,析构函数被调用多次,pData被删除多次,程序崩溃
// //规避方式:判断pData是否为空,非空才delete[] pData
// //但在不知情的情况下使用pData仍然会出问题,因此浅拷贝导致的问题不可规避
// cout << "~cstudent" << endl;
// delete[] pData;
// pData = NULL;
// }
//};
//
//void TestSTL(){
//
// auto mp = new std::map<int, std::string>();//c++11新风格 auto
//
// mp->insert({ 10, ("h你好") });//c++11新风格,不用再使用std::pair()或std::make_pair()
// mp->insert({ 20, "el" });
// for (auto var : *mp)//c++11新风格for
// {
// std::cout << var.first << "," << var.second << "," << std::endl;
// }
//}
//
//void TestClass(){
// CBase* pbase = new CStudent();
// auto pst = (CStudent*)pbase;
// pst->SetName("xxxx");
// auto name = pst->GetName();
// delete pbase;
//}
//
//int TestAdd(int a, int b){
// return a + b;
//}
//void TestStdFunc(std::function<int(int,int)> fun, int a, int b){
// auto ret = fun(a, b);
//}
//
//typedef int(*TestAddPtr)(int, int);
//
//void TestPCall(TestAddPtr func, int a, int b){
// auto ret = func(a, b);
//}
//
//struct Vertex{
// bool isgood;
// float x, y, z;
// double dx;
// bool bx;
// int ix;
// bool by;
//};
//void TestFile(){
// int szChar = sizeof(char);
// ofstream ofs;
// ofs.open("f:/test.txt");
// ofs << "hello " << 10 << " world " << 20 << endl;
//
// ofs.flush();
// ofs.close();
//
// ifstream ifs;
// ifs.open("f:/test.txt");
// string str1, str2;
// int num1, num2;
//
// ifs >> str1 >> num1 >> str2 >> num2;
//
// //错误示例:二进制读写,使用std::<<或>>进行的还是ASCII码的读写
// ofstream ofsb;
// ofsb.open("f:/testb", ios::binary);
// ofsb << "hellob" << 1022;
//
// ofsb.flush();
// ofsb.close();
// ifstream ifsb;
//
// string sx;
// int nx;
// ifsb.open("f:/testb", ios::binary);
// ifsb >> sx >> nx;
// ifsb.close();
//
// //正确做法
// sx = "binary";
// nx = 978;
// ofsb.open("f:/testbx", ios::binary);
// ofsb.write(sx.c_str(), sx.length()*sizeof(char)+1);
// ofsb.write((const char*)&(nx), sizeof(int));
// ofsb.flush();
// ofsb.close();
//
// char sxr[32];
// int nxr;
// ifsb.open("f:///testbx", ios::binary);//注意这里的"///"不管有多少个/都等同于一个
// ifsb.read(sxr, sx.length()+1);
// ifsb.read((char*)&nxr, 4);
//
// //数据转换的更通用方式
// Vertex vt;
// vt.bx = true;
// vt.isgood = false;
// vt.x = 12;
// vt.y = 13;
// vt.z = 14;
// vt.dx = 3.9;
// vt.by = 0;
//
// ofstream ofsbx;
// ofsbx.clear();
// ofsbx.open("f:/testbyx2", ios::binary);
// ofsbx.write((const char*)&(vt), sizeof(Vertex));
// ofsbx.flush();
// ofsbx.close();
//
// ifstream ifsbx;
// Vertex vrt;
// ifsbx.clear();
// ifsbx.open("f:/testbyx2", ios::binary);
// ifsbx.read((char*)&vrt, sizeof(Vertex));
//
// string s1 = "hello";
// string s2 = "wrold";
// s1 = s1 + s2;
// auto s3 = s1.substr(1, 2);
//}
//
////实现较为高效的字符串分割,限制是分割符只能是一个字符,不能是一个串
//std::list<string> TestStrSplit(string s, char sep){
// std::list<string> lst;
// for (int i = 0, j = 0; i < s.length(); ++i){
// if (s[i] == sep){
// lst.push_back(s.substr(j, i - j));
// j = i + 1;
// }
// }
//
// //注意临时对象作为返回值了,一般情况下这是错误的用法,栈上的临时对象出了函数域后会被释放
// //但这里STL容器内部重载了=运算符,作了值拷贝就没问题了
// return lst;
//}
//void TestString(){
//
// //g正则表达式实现字符串分割
// string s1 = "a;b;c;dddd;ef;";
// string s2 = "a123b2673cdd4444a";
// std::regex re("(\d+)");
// std::smatch mtch;
//
// //这个做法效率挺低且浪费内存,产生了很多中间字符串
// while (std::regex_search(s2, mtch, re, std::regex_constants::match_default)){
// cout << mtch.str() << endl;
// s2 = mtch.suffix();
// }
//
// //这个函数效率要高多了
// auto lst = TestStrSplit(s1, ';');
//
//}
//
////返回栈上的临时对象测试
//CStudent TestTempObjRet(){
// CStudent ost; //临时对象
// return ost; //调用对象的拷贝构造函数
//}//出了栈后ost被释放,析构函数调用,同时成员对象被析构CStudent.name="",但内置类型仍保持原值
//
////通过测试可知,将栈上对象作为函数返回值使用一般是没有问题的,但浅COPY时两个对象中的指针指向同一份
////内存,当一个对象被删除时,另一个对象中的指针就指向非法位置了,成了野指针
//void TestObjConstructorAndDestructor(){
// CStudent ostx;
// ostx = TestTempObjRet(); //调用拷贝构造函数(与上面对应)
// auto name = ostx.GetName();
// auto px = ostx.pData;
//}
//
//void TestRRef(){
//
//}
//
////可以使用随机访问(数组下标)说明vector在内存中是连续存放的
////这样,vector在需要扩充容量时就需要将原来内存删除,再申请一块新内存
////但这并不一定,因为内存申请时若用realloc则有可能会在原内存后面增加(原理)
//void TestVector(){
// std::vector<string> sv{ "hello", "world" };
// sv[0];
// sv[1];
//
// sv.reserve(20); //旧的内容被清除
// int n = sv.capacity(); //20
// sv.push_back("a");
// sv.push_back("b");
// sv.clear(); //旧的内容被清除
// n = sv.capacity(); //20
//
// sv.shrink_to_fit(); //内存释放
// n = sv.capacity(); //0
//
//}
//
//struct CTA{
//private:
// virtual void Test(){
// cout << "cta" << endl;
// }
//
//};
//
//class CCTA : CTA{//类和结构体可以相互继承
//public:
// int _id;
// void Test() const{
// cout << "ccta-test" << endl;
// }
//};
//
////C++中字符串有常量和变量之分,字符串遇到\0则结束
////C#中只有常量字符串,字符串遇到\0不结束,视其为正常字符
//void TestStr(){
// char* ps = "hello";//字符串常量,不可修改其内容
// ps[0] = 'd'; //运行出错
//
// char arr[] = "hello"; //字符串变量
// char* par = arr;
// arr[0] = 'd'; //ok
//}
//
////C++中指针字符串与数组字符串都是自动以0结尾的
//void TestMemcpy(){
//
// char dest[18];
// char src[] = "hell"; //以0结尾,长度为5,若强制声明为 char src[4] = "hell"则编译报错
// char* psrc = "hell"; //以0结尾,长度为5,但测试长度strlen(psrc)为4,因为它没算尾0
//
// for (int i = 0; i < 10; ++i){
//
// }
// for (int i = 0, ch; (ch = psrc[i++]) != 0;){
// //这里发现字符串尾0后有许多个0,不知道原因
//
// }
// auto len = strlen(psrc); //4,测试长度,并没字符串的真实长度(内存中真实串),因为它有尾0
// int len2 = strlen(src); //5,字符串实际长度(内存中存储的字符串)
// int st = sizeof(src); //5,数组大小
// memcpy(dest, psrc, strlen(psrc)+1);
//}
//template<typename T1, class T2> class MyVector{
// std::vector<int> _lst;
//
//public:
//
// void Test2();
//};
//
//template<class T1, class T2> void MyVector<T1, T2>::Test2(){
//
//} #pragma region 2018.7.7
[module(name = "mytestx")];
void TestIOStream() {
std::fstream fs;
fs.open("test.txt", ios_base::in | ios_base::out);
fs << << "hello"; fs.seekp();
int ix1;
string sx1;
char chs[];
fs >> ix1;
fs >> chs;
chs[] = ;
sx1 = chs; cout << ix1 << sx1.c_str() << endl; }
void TestMacro() {
#define hfunc(x) cout << x << endl; //自定义处起,全局可见
hfunc();
#undef hfunc //typedf, using等价使用
typedef void(*PFUN)(int);
using PFUNC = void(*)(int); using Int = int;
using MyType = Int;
}
//数组和指针
void TestArrayAndPointer() {
//1,char* p : char类型指针,指向char数组, p++移动一个char
//2,int* p : int型指针,指向int数组,p++移动一个int
//3,char(*p)[2] : char[2]类型指针,指向char[2]类型数组,即char[][2]数组,p++移动一个char[2]
//总结:X类型的指针指向X类型的数组, p++移动一个数组元素
//如何看指针类型:去除*p剩下的就是类型,如char*p去掉*p为char,char(*p)[2]去掉*p为char[2] //========================================================
//指针总是指向数组的,如下,可认为是指向只有一个元素的数组
//========================================================
int ix = ;
int*pix = &ix;
cout << pix[] << "," << *pix << endl; //========================================================================================
//堆和栈上数组的初始化列表写法
//========================================================================================
char arr[] = { 'a','b','c' };
char arr2[] = { "hello" };
int iarr[] = { , , , };
char*ps = new char[]{ };
int* ips = new int[]{};
int* ips2 = new int[]; //cout << arr << "," << (void*)arr << (void*) ps << endl;
char* px;
px = arr; //可以赋值,说明数组名与指针等价
const char* cp;//可以cp++;
char* const cpx = arr; //不可以 cpx++,不能移动的指针,数组名其实就是这种指针 //这里以arr与ps作对比,数组名与指针本质上都是指针,只是数组名是不能移动,不能赋值的常指针
//在二维情形时也是如此 stringstream ss;
//========================================================================================
//1,栈上二维数组,【内存连续】
//========================================================================================
char a[][] = {//二维数组初始化列表
{ , , },
{ , , },
};
for (int i = ; i < ; ++i) {//验证
ss << *(*a + i) << ",";
}
cout << ss.str() << endl; //=============================================================================
//2,数组指针(也称行指针),【内存连续】
//=============================================================================
int(*pax)[] = new int[][];
for (int i = ; i < ; ++i) {
for (int j = ; j < ; ++j) {
pax[i][j] = i * + j + ;
}
} ss.str("");
for (int i = ; i < ; ++i) {//验证
ss << *(*pax + i) << ",";
}
cout << ss.str() << endl; //=============================================================================
//3,指针数组,【内存不连续】
//=============================================================================
//因为它是一个数组,所以不能用new来给它分配内存,new出来的东西只能赋值给指针
char* arr_p[];
arr_p[] = new char[]{ 'h','e','o','l','l' };
arr_p[] = new char[]{ 'a','b','c' }; //=============================================================================
//4,多级指针用来分配二维数组,有【连续内存分配法】和【不连续内存分配法】
//这个非常重要,若用一个不连续的二维数组指针进行memcpy操作,则会发生严重问题:
//(1)数据拷越界,覆盖了其它变量甚至程序的内存
//(2)dest变量中数据只填充了一部分,其余部分还是旧数据,导致程序出现莫名其妙的问题
//(3)这种数据拷越界并无任何提示,隐蔽性极高,非常难以查找
//=============================================================================
int**pi = new int*[];
int* ptemp = new int[];
for (auto i = ; i < ; ++i) {
//------------------------------------------------
//(1)【不连续内存分配法】
//pi[i] = new int[2]; //------------------------------------------------
//(2)【连续内存分配法】
pi[i] = &((ptemp + i * )[]);
for (int j = ; j < ; ++j) {
pi[i][j] = i * + j;
}
}
for (int i = ; i < ; ++i) {//验证
for (int j = ; j < ; ++j)
{
ss << pi[i][j] << ",";
}
}
cout << ss.str() << endl; }
void TestInitialist() {
class CIn {
public:
float x, y, z;
string name; }; //初始化列表的使用条件:
//无自定义构造函数,成员公有,无基类,无虚函数
//这么多限制,可以说很鸡肋
CIn oin = { , , , "hello" }; //方式1
CIn oin2 { , ,, "world" }; //方式2
}
#pragma endregion #pragma region 2018.7.9
class CComplex {
float real, image;
public:
CComplex(float real, float image) {
cout << "constructor: " << real << "," << image << endl;
this->real = real;
this->image = image;
} CComplex(const CComplex& other) {
cout << "copy constructor: " << other.real << "," << other.image << endl;
if (this != &other)
{
real = other.real;
image = other.image;
}
}
~CComplex() {
cout << "~ccomplex" << "(" << real <<"," <<image << ")" << endl;
// real = 0;
// image = 0; } void PrintInfo() {
cout <<"Complex: " << real << "," << image<< endl;
} public: //-------------------------------------------
//运算符重载
//-------------------------------------------
//1,重载为成员函数
CComplex operator+(const CComplex& other) {
cout << "operator+" << endl;
return CComplex(real+other.real, image + other.image);
} CComplex& operator++() {//前向++
cout << "forward ++ " << endl;
real++; image++;
return *this;
}
CComplex& operator++(int) {//后向++
cout << "backward ++ " << endl; real++; image++;
return *this;
}
const CComplex& operator=(const CComplex& other) {
this->real = other.real;
this->image = other.image;
return *this;
}
//2,重载为友元函数
friend CComplex operator+(float fx, const CComplex& cp); //3,【运算符重载函数不能定义为静态函数】
//这与C#不同,C#中所有运算符重载都必须是public和static的
//static CComplex operator+(float fx, const CComplex& cp); //4,类型转换运算符重载
operator bool() {//使用情景:CComplex oc; if(oc){}或if(oc != NULL){}或 float/int/bool x = oc
return real != && image != ;
}
operator float() {//使用情景:CComplex oc; if(oc){}或if(oc != NULL){}或 float/int/bool x = oc
return real;
} // CComplex operator=(const CComplex& other) {
// if (this == &other)
// return other;
// return CComplex(other.real, other.image);
// } void Testx() {
CComplex* pNewCom = new CComplex(, );
pNewCom->real = ;//可以访问私有成员??
}
};
// CComplex CComplex::operator+(float fx, const CComplex& cp) {
// return CComplex(fx + cp.real, cp.image);
// }
CComplex operator+(float fx, const CComplex& cp) {
return CComplex(fx + cp.real, cp.image);
} void TestCComplexOper() {
int i = ;
CComplex cpx(, );
++cpx++++;
cpx.PrintInfo();
}
CComplex TestReturnStackObj() {
//-----------------------------------------------------------------
//返回栈上的对象 stackObj
//返回栈上的对象会导致拷贝构造函数的调用,生成一个
CComplex stackObj(, );
return stackObj; return CComplex(, ); //这种方式直接调用构造函数,而不调用拷贝构造函数
//-----------------------------------------------------------------
} #pragma endregion #pragma region 2018.7.10
void TestRealloc() {
cout << "---------------test-realloc---------------" << endl; int szch = sizeof(char);
char*pstr = "this is a test str";
int strLen = strlen(pstr); char* pdesc = (char*) malloc((+strLen)* sizeof(char));
for (int i = ; i < strLen; ++i) {
cout << "," << hex<< (int)pdesc[i];
}
cout << endl; cout << strlen(pstr) << endl; strcpy_s(pdesc, strLen+, pstr); for (int i = ; i < strLen; ++i) {
if(pdesc[i] > )
cout << (char)pdesc[i];
else cout << "," << (int)pdesc[i] ;
} cout << endl; pdesc = (char*)realloc(pdesc, );
for (int i = ; i < ; ++i) {
pdesc[strLen + i] = 'a' + i;
} for (int i = ; i < + strLen; ++i) {
if (i < strLen)
cout << pdesc[i] << ",";
else
cout << (unsigned short)pdesc[i] << ",";
}
cout << endl; cout << "---------------test-realloc---------------" << endl;
} template<typename T> class CMyNumOperator {
T a, b;
public:
static T Add(T x, T y) {
return x + y;
}
};
#pragma endregion #pragma region 2018.7.11
#pragma region 继承相关
class A {
public:
A(int x) {
fProtected = x;
}
float GetFProtected() {
return fProtected;
} public:
float fpublic = 2.3f; //c++11支持了初始化,但不能使用auto
string sname = "liqi";
CMyNumOperator<int>* on = new CMyNumOperator<int>(); //对象也可以 void TestFunc() {
cout << "TestFunc" << endl;
} static void StaticTestFunc() {
cout << "Static-TestFunc" << endl;
}
virtual void ToString() {
cout << "A::ToString" << endl;
}
protected:
float fProtected;
void ProtectedFunc() {
cout << "PRotectedFunc" << endl;
}
private:
void PrivateFunc() {
cout << "PrivateFunc" << endl; } }; //只管公有继承,不管保护继承和私有继承,意义不大,也太复杂
class B : public A {
public:
friend void TestProtectedDerive();
B() :A() {}
void TestForDerive() {
//公有继承下
//1,子类可以访问父类的保护成员,不能访问父类的私有成员
B ob;
//PrivateFunc(); //error,子类不能访问基类的私有成员
ProtectedFunc(); //right
fProtected = ; //right
ob.fProtected = ; //right
} //1,c++中只要基类有相同签名虚函数,则默认为此基类函数也是虚函数[与C#不同],如下情形都成立
// (1) 函数不声明 virtual
// (2) 函数声明了 virtual
// (3) 函数声明了 override
// (4) 函数声明了 virtual 和 override
//2,c++中两个关键词作用不同,可以同时存在
// virtual仅表明函数是虚函数,override是C++11中出现的,明确说明是对基类的重写
// 它的好处是当函数声明不符合规则时,编译器会报错
void virtual ToString() override{
cout << "B::ToString" << endl;
}
}; void TestProtectedDerive() {
B ob;
ob.ProtectedFunc();
} #pragma endregion
#pragma endregion
#pragma region 2018.7.18
#pragma region 标准输入流
void TestCinCout() {
float fx;
std::string str;
while (true) {
bool errorNum = false;
cin >> str; //1,试读,看是不是"exit"串
if (str == "exit")//2,若是,结束循环
break;
for (int i = str.length() - ; i >= ; --i) {//3,若不是,将串放回到流中,注意是反向放回的
cin.putback(str[i]);
} cin >> fx;
if (cin.fail()) {//4,如果格式错误
cout << "格式错误:请输入一个数值" << endl;
cin.clear(); //5,清除错误标识
while (cin.get() != '\n'); //6,读掉后面出错的所有字符,直到回车
errorNum = true;
} if (!errorNum) {//7,若前面输入(数字)是正确的,则继续后面的解析
cin >> str;
if (cin.fail()) {
cout << "格式错误:请输入一个字符串" << endl;
cin.clear();
}
cout << ">>数值= " << fx << ", 描述= " << str << endl;
} } }
#pragma endregion
#pragma region 计算机数据存储
void TestComputeDataStorage() {
//数据转换:C++,C# 通用
//1,整形数据:短数据类型转长数据类型时,正数高位补0,负数高位补1
//2,浮点形数据转整形时,得到整数部分,舍去了小数部分 cout << hex;
cout << (int)(short) << endl; //1,即 0x00000001
cout << (int)(short)- << endl; //0xffffffff,即负数高位补1
cout << - << endl; //0xffffffff,负数表示法,符号位1,真值(1)求补码 auto sz = sizeof(long);//64位系统,X64编译器下VS2017测试值为4
float fx = 83.7f;
auto lfx = (long unsigned int)fx; //浮点转整形,
long long x; //8位整形
long unsigned int lui; //8位无符号整形 //浮点数据字节察看
//125.5f = 0x42fb0000
//-125.5f = 0xc2fb0000
//83.7f = 0x42a76666
//浮点数存储按IEEE754标准:
//以float为例:共4个字节,从高位到低位依次是31,30,...2,1,0
//最高位存放数据符号,接下来8位存放阶码(包括阶码符号位),接下来23位存放尾数
int ifx = *(int*)(&fx);
//等价于
int* pfx = (int*)&fx;
int ipfx = *pfx; int sz2 = sizeof(x);
} #pragma endregion
#pragma region 地址与指针
void TestAddrAndPointer() {
//-------------------------------------------------------------
//1,&p, p, *p的区别: &p是p的地址,p是一个地址,*p是地址中的内容
//2,地址与指针完全等价,有两种操作:*地址,地址->
//3,地址就是一个数值,指针也是个地址
int x = ;
*(&x) = 0x100;
*((char*)&x) = ; //小端模式下[低字节存低地址处,高字节存高地址处]:0x101
int* pxt = (int*); //直接指向内存地址0x0000000a处
int*px = &x; //px与 &x完全等价
int adr = (int)(&x); //地址就是个数值,指针也是个地址值
px = (int*)adr; cout << hex; //输出为16进制
cout << adr << "," << &x << "," << (int*)&x << "," << px << endl; //四者等价,输出相同值
cout << dec; //输出为10进制 A oa();
(&oa)->fpublic = ; //地址与指针等价
(*(&oa)).fpublic = ; //地址与指针等价 }
#pragma endregion
#pragma region 函数指针
void TestFuncPtr() {
cout << "TestFuncPtr" << endl;
}
void TestFuncPtrParam(int, int, int) {//注意函数参数可以不写变量名
void(*pf)(int, int, int) = TestFuncPtrParam;
int*p = (int*)pf; //试图找出函数实参,失败,对函数汇编原理不清楚,有时间再查
cout << *(p) << "," << *(p-) << endl;
}
void TestFuncPointer() {
A oa();
//1,函数指针与普通指针不兼容,不能相互强转
//2,函数指针赋值方式有二:pf = func或 pf = &func
//3,函数指针pf使用方式有二:pf()或 (*pf)(),因为pf和 *pf的值相同,调试模式下可以看到 //1,普通成员函数指针
typedef void(A::* PFUNC)(void); //函数指针声明方式一
using PFunc = void(A::*)(void); //函数指针声明方式二,C++11新方式 PFunc pf = &(A::TestFunc);
int pfsz = sizeof(pf);
(oa.*pf)(); //2,全局函数指针
void(*pfg)() = TestFuncPtr;
pfg();
(*pfg)(); //3,静态函数指针
void(*sptf)() = A::StaticTestFunc;
sptf();
(*sptf)();
}
#pragma endregion
#pragma region 虚函数表原理
void TestVirtualFunctionTable() {
cout << hex;
typedef void(*PFUNC)(); offsetof(A, fpublic); //利用此函数可以算函数布局 A oa();
B ob; //一,通过内存地址修改不可访问的保护变量
*(float*)((int*)&oa + ) = 123.4f; //类的第一个变量fpublic赋值,(int*)&oa + 1是跳过虚函数指针
float fpublic = oa.fpublic; //二,通过内存地址调用虚函数
//A和B的虚函数表地址不一样,也就是说父类和子类各有一张虚函数表
int* pvptr = (int*)(*((int*)(&oa)));
cout << "A的虚函数表地址:" << pvptr << endl; //000DB0D4
((void(*)())(*pvptr))(); //A::ToString pvptr = (int*)(*((int*)(&ob)));
cout << "B的虚函数表地址:" << pvptr << endl; //000DB128
((void(*)())(*pvptr))(); //B::ToString cout << "--------------------------" << endl;
//最简写法
((void(*)())(*((int*)*(int*)&oa)))();
((void(*)())(*((int*)*(int*)&ob)))(); }
#pragma endregion
#pragma region 函数对象,友元函数模板运算符重载
template<class T>
class AddTwoNumber {
public:
T x; AddTwoNumber(T x) {
this->x = x;
}
public:
//【通过重载()运算符,实现函数对象】
T operator()(T a, T b) {
return a + b;
} //一,使用模板类型的友元模板函数
//1, <>表示该友元是一个模板函数,且使用本模板类的类型
// 若不加<>说明符,则找不到模板函数定义,运行时出错
//2,这里的T是模板类传来的类型,因此,这里不能实现与T不同的类型操作
//比如若T为int,则 2.1f + new AddTwoNumber<int>()不合法
//3,【注意这里第二个参数是个引用类型,若是AddTwoNumber<T>对象类型则会出错,不能在类中定义本类对象】
friend void operator+ <>(T os, AddTwoNumber<T>& n); //二,使用模板函数自带类型的友元模板函数
//这里的T是一个新的类型,与此模板类的T没关系,因此没有上面的限制
template<class T>
friend void operator+(T os, A oa); template<class T>
T Add(T a, T b);
}; template<class T>
void operator+ <>(T os, AddTwoNumber<T>& n) {
cout << "operator+: n + AddTwoNumber: " << os << endl;
} template<class T>
void operator+(T n, A o) {
cout << "operator+: n + A : " << n << endl;
} //==================================================
//※※※※※※注意这种多层的模板前置声明※※※※※※※
template<typename T> //类模板的前置声明
template<typename T1> //函数模板的前置声明
T1 AddTwoNumber<T>::Add(T1 a, T1 b) {
return a + b;
} void TestAdd2Num() {
AddTwoNumber<double> NumAdd();
auto nadd = NumAdd(, );
A oa();
2.1f + oa; //左操作数任意数值类型,因为使用的是模板函数自带类型
2.0 + NumAdd;//左操作数必须为double, AddTwoNumber<string> add2("str");
add2.Add(, );
cout << "x: " << add2.x << endl;
}
#pragma endregion
#pragma endregion
#pragma region 2018.7.19
#pragma region 智能指针 //---------------------------------------------------------------------------------------------- template<typename T>
class SmartPointerx {
private:
T * _ptr;
size_t* _count;
public:
SmartPointerx(T* ptr = nullptr) :
_ptr(ptr) {
if (_ptr) {
_count = new size_t();
}
else {
_count = new size_t();
}
} SmartPointerx(const SmartPointerx& ptr) {
if (this != &ptr) {//永远成立
this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++;
}
} SmartPointerx& operator=(const SmartPointerx& ptr) {
if (this->_ptr == ptr._ptr) {
return *this;
} if (this->_ptr) {
(*this->_count)--;
if (this->_count == ) {
delete this->_ptr;
delete this->_count;
}
} this->_ptr = ptr._ptr;
this->_count = ptr._count;
(*this->_count)++;
return *this;
} T& operator*() {
assert(this->_ptr == nullptr);
return *(this->_ptr); } T* operator->() {
assert(this->_ptr == nullptr);
return this->_ptr;
} ~SmartPointerx() {
(*this->_count)--;
if (*this->_count == ) {
delete this->_ptr; //数组内存泄漏 int*p = new int[10]
delete this->_count;
}
} size_t use_count() {
return *this->_count;
}
}; void TestSmartPtr() {
{
SmartPointerx<int> sp(new int());
SmartPointerx<int> sp2(sp);
SmartPointerx<int> sp3(new int());
sp2 = sp3;
std::cout << sp.use_count() << std::endl;
std::cout << sp3.use_count() << std::endl;
}
//delete operator
}
//---------------------------------------------------------------------------------------------- //下面是一个简单智能指针的demo。智能指针类将一个计数器与类指向的对象相关联,
//引用计数跟踪该类有多少个对象共享同一指针。每次创建类的新对象时,初始化指针并将引用计数置为1;
//当对象作为另一对象的副本而创建时,拷贝构造函数拷贝指针并增加与之相应的引用计数;
//对一个对象进行赋值时,赋值操作符减少左操作数所指对象的引用计数(如果引用计数为减至0,则删除对象),
//并增加右操作数所指对象的引用计数;调用析构函数时,构造函数减少引用计数(如果引用计数减至0,则删除基础对象)。
//智能指针就是模拟指针动作的类。所有的智能指针都会重载->和 * 操作符。
//智能指针还有许多其他功能,比较有用的是自动销毁。
//这主要是利用栈对象的有限作用域以及临时对象(有限作用域实现)析构函数释放内存
template<class T>
class SmartPointer final{ //final
T* pobj = NULL;
__int64* refCnt = ;
public:
SmartPointer(T* pobj) {//这里可能会传一个栈对象地址
if (pobj) {
if (pobj != this->pobj) {
if (!this->pobj)
this->pobj = new __int64;
(*refCnt)++;
this->pobj = pobj;
}
}
} SmartPointer(const SmartPointer<T>& rhs) {
operator=(rsh);
} SmartPointer<T> operator=(const SmartPointer<T>& rhs) {
if (this == &rhs || pobj == rhs.pobj)
return rhs;
(*refCnt)--;
(*rhs.refCnt)++;
pobj = rhs.pobj;
return *this;
} ~SmartPointer()
{
refCnt--;
if(refCnt == )
ReleaseRes();
} T* GetPtr() const {
return pobj;
} private:
void ReleaseRes() {
if (pobj) {
try
{
delete[] pobj;
pobj = NULL;
}
catch (const std::exception&)
{
cout << "智能指针指向的不是一个堆对象" << endl;
}
}
}
};
#pragma endregion
#pragma endregion #pragma region 2018.7.23
#pragma region 数组传参方式 //方式一,数组引用传递
template<int N>
void ArrayRefAsParam(char(&_dest)[N]) {//数组引用的写法
char chs[] = "hello";
char* pstr = "hello";
cout << sizeof(chs) << endl;
cout << strlen(chs) << ", " << strlen(pstr) << endl; strcpy_s(chs, "world");
cout << chs << endl;
} //方式二,指针传递
void PointerAsParam(const char* pArr, int elemCount) {
} void TestAstrPstr() {
char chs[] = "world"; //6个字符,自动加了一个尾0 //1,数组引用传参,以下两种方式等价
ArrayRefAsParam(chs); //模板不仅可以推导类型,也可以推导出数组的大小
ArrayRefAsParam<>(chs); //说明了模板的工作原理,可以不写6,模板自动推导出数组大小 //2,指针传递
int sz = sizeof(chs); //
int slen = strlen(chs); //
PointerAsParam(chs, + strlen(chs));
}
#pragma endregion
#pragma region 静态(变量与函数)与常量(常引用,常指针,常函数)
class CWithConstStatic {
private:
static int _privateId;
public:
string _str = "CWithStatic";//C++11,可以这样初始化
static string _sstr; //静态变量不允许在类内初始化,这与旧C++一致
int _id = ;
public:
static void StaticMethod(){
//1,静态函数本质上是一个全局函数
//2,静态函数不能访问非静态变量和非静态函数,包括常函数及常量,因为它不属于对象,没有this指针,编译器翻译时出错
// _id = 10; //不访问非静态变量,因为没有this指针,不翻译为this->_id
//ConstMethod();//不能访问非静态函数,因为没有this指针,不翻译为 this->ConstMethod()
}
void ConstMethod() const {//
auto id = this->_id;
StaticMethod(); //可以访问静态函数,因为静态函数不可能更改对象
//NormalMethod(); //不能访问普通函数,因为普通函数可能会更改对象
} void ConstMethod() {
//注意1和2的两个ConstMethod函数是重载关系
} void NormalMethod() {//若函数从【调用1】处进入,则有:
cout << "normal method begin" << endl; //输出,没问题
//cout << _id << endl; //出错,因为这里等价于 this->_id,而this指针为NULL
}
}; string CWithConstStatic::_sstr; //静态变量在类外的CPP中声明
void NormalMethod(CWithConstStatic* _this) { } void TestCWithStatic() { //1,常对象
const CWithConstStatic ow;
//ow._id = 1001; //error, 常对象不能被修改
//ow._str = "dd"; //error, 常对象不能被修改
ow._sstr = "dd"; //ok, 静态变量不属于对象 //2,常引用
const CWithConstStatic& owRef = ow;
//owRef._str = "hhh"; //error, 常引用不能被修改对象
owRef._sstr = "dd"; //ok, 静态变量不属于对象 //3,常量指针,指向常量的指针,指向的内容不可修改
const CWithConstStatic* pcwcs = new CWithConstStatic();
//pcwcs->_id = 20; //error,不可通过常指针更改其指向的内容 //4,指针常量,指针是一个常量,不可被再次赋值
CWithConstStatic* const cpcwcs = new CWithConstStatic();
cpcwcs->_id = ; //ok //5,类函数原理,this指针
//c++类的成员函数被编译器翻译为了C语言编译器可以识别的全局函数,然后用C语言编译器来处理它 //以下两条调用等价
CWithConstStatic* pwcs = NULL;
pwcs->NormalMethod(); //【调用1】C++的样子
NormalMethod(pwcs); //【调用2】C语言翻译出来的结果 }
#pragma endregion
#pragma region 线程
void ThreadFunc() {
cout << "thread func 1 : " << _threadid<< endl;
//Sleep(1000);
}
void TestThread() {
std::thread t(ThreadFunc);
cout << _threadid << endl;
t.join();
}
#pragma endregion
#pragma region 深入模板
#pragma region 可变参数模板
void TestVarTemp() {//【无参的重载函数】
//这个函数必须定义,否则编译器报错,因为函数参数展开时,最终(可变参数个数为0时)要调用此函数
} template<typename First,
typename ... Args
>
void TestVarTemp(First first, Args... args) {
//sizeof...是可变参数模板专用的获取参数个数的函数
cout << sizeof...(args) << "-" << first << " "; //可变参数展开的唯一方式是递归调用,一层层剥离参数,当参数个数为0时调用无参的重载函数,见【无参的重载函数】
TestVarTemp(args...);
}
void TestVarTemplate() {
TestVarTemp(, , , );
}
#pragma endregion
#pragma endregion
#pragma region 构造和拷贝构造
class CNormclass {
public:
CNormclass() {
cout << "constructor" << endl;
}
CNormclass(const CNormclass& rhs) {//有了复制构造函数后,系统不再为类生成无参构造函数
cout << "copy-constructor" << endl;
*this = rhs;
}
}; CNormclass TestConstructorAndCopyCon1() {
return CNormclass();//不调用COPY构造函数
}
CNormclass TestConstructorAndCopyCon2() {
//对象定义:两种不同的定义方式
//方式一,会调用两次构造函数
CNormclass r0; //constructor
r0 = CNormclass(); //constructor,注意不是COPY构造函数 //方式二,只调用一次构造函数
CNormclass rr = CNormclass(); //constructor //COPY构造函数仅在两种情况下调用:
//1,将一个已存在的对象生成另外一个对象
CNormclass r1 = r0; //拷贝构造
//2,将一个已存在的对象作为参数传递给构造函数时
CNormclass r2(r0); //拷贝构造 //不调用构造函数,也不调用拷贝构造函数,也不调用=运算符(因为是同类型),只是进行按位copy
r1 = r0; cout << "before return " << endl;
return rr; //调用COPY构造函数
}
#pragma endregion
#pragma region 函数指针复杂嵌套
typedef int(*PF2)(int);
typedef PF2(*PF1)(int, int);
typedef PF1(*PF)(int);
int func2(int) {
cout << "func2" << endl;
return ;
}
PF2 func1(int, int) {
cout << "func1" << endl;
return func2;
}
PF1 funcx(int) {
cout << "funcx" << endl;
return func1;
} void TestNestingFuncPtrs() {
//1,一次嵌套
PF1 pf1 = func1;
pf1(, )(); //等价方式的直接声明
int(*(*ptr)(int, int))(int) = func1;
ptr(, )(); cout << "--------------------" << endl; //2,二次嵌套
PF pf = funcx;
pf()(, )(); //等价方式的直接声明
int(*((*((*ptr2)(int)))(int, int)))(int) = funcx;
ptr2()(, )();
}
#pragma endregion
#pragma region 类型转换构造函数
class CTypeCast {
public:
int _id;
string _name;
CTypeCast(int i) {//整形转换构造函数:将一个整形转为对象
_id = i;
cout << "integer cast " << i << endl;
}
CTypeCast(string str) {//字符串转换构造函数:将一个字符串转为对象
_name = str;
} //注意,显示声明,转换必须显式进行
explicit CTypeCast(float fx) {//浮点转换构造函数:将一个字符串转为对象
cout << "float cast " << fx << endl;
}
}; void TestTypecastContructor() {
//CTypeCast otc = 1; //整形转换构造函数
//CTypeCast otc2 = "otc2"; //字符串转换构造函数
//otc = 3; //注意,当加了explicit后,类型转换必须显示进行,因此下面这个语句不会使用浮点转换构造函数
//但是,它却可以使用整形转换构造函数,这会造成数据精度丢失
CTypeCast otc3 = 3.2f; //隐式转换:整形转换构造函数
CTypeCast otc4(3.2f); //显示转换:浮点转换构造函数 }
#pragma endregion #pragma region 2018.7.24
#pragma region 类型转换运算符及()[]重载
class CTypeCastOper{
float fx = 0.2f;
int arr[]{ ,, };
public:
//1,类型转换运算符
explicit operator float() {
return fx;
}
operator string() {
} //2,()重载
//()运算符并不是用来做类型转换的,它是当函数用的,即仿函数,或函数对象
bool operator()() {
return true;
} //3,[]重载
//[]运算符与()差多的用法,都是用于对象之后
int operator[](int idx) {
return arr[idx];
}
}; void TestTypecastOper() {
CTypeCastOper oper;
float fx = (float)oper;
cout << fx << endl; //1,()运算符
bool b = oper();
//2,[]运算符
cout << oper[] << "," << oper[] <<"," << oper[] << endl;
}
#pragma endregion
#pragma region 模板特化
template<typename T>
class CTehuaTemp {
public:
T px = "abc";//2,被特化为了一个char*类型指针,故可以这样用
};
template<typename T>
class CDThhuaTemp : public CTehuaTemp<T*> {//1,将基类模板参数特化为一个指针类型
public:
T ch = 'c';
}; void TestTehuaTemp() {
CDThhuaTemp<char> otp;
cout << otp.px << endl;
cout << otp.ch << endl;
}
#pragma endregion
#pragma region 同类型赋值,常引用修改
class CSimpleclass {
public:
CSimpleclass() {
cout << "cons" << endl;
} CSimpleclass(const CSimpleclass& rhs) {
cout << "copy cons" << endl;
}
public:
float fx = ; //默认未初始化,给它来个初始化
};
void TestSameTypeAssign() { CSimpleclass oc, oc1;
const CSimpleclass& oc2 = oc;
const CSimpleclass& oc3 = oc; cout << "-------------------------" << endl;
//【同类型赋值,不调用=运算符,也不调用任何构造函数】
oc1 = oc; //oc2 = oc3; //常引用本身是个常量,也不能被修改
//oc2 = oc1; //常引用本身是个常量,也不能被修改
//oc2.fx = 30; //常引用不能更改引用的对象内容 const std::string ss;
//ss = "abc"; //wrong
//ss.clear(); //wrong
}
#pragma endregion
#pragma region 堆指针栈指针判断
class CTestPointerType {
public:
CTestPointerType(float fx=) {
this->fx = fx;
}
float fx;
}; template<class T, int N>
class CHeapDebugger {
public:
static void Print(const T* p){
int sz = N * sizeof(T); int* ip = (int*)p;
int headFlag = *(ip - );
int endFlag = *(int*)((char*)ip + sz);
int orderFlag = *(ip - );
int szFlag = *(ip - ); bool isHeapPtr = headFlag == endFlag && headFlag == 0xfdfdfdfd && sz == szFlag;
cout << "----------------------------------------------" << endl;
if (isHeapPtr) {
cout << hex << "堆大小:" << szFlag << endl;
cout << "堆编号: " << orderFlag << endl;
cout << "堆首界: " << headFlag << endl;
cout << "堆尾界: " << endFlag << endl;
}
else {
cout << "栈指针" << endl;
}
cout << "----------------------------------------------" << endl; }
};
void TestPointerType() {
//
const int N = ;
int*p = new int[N];
for (int i = ; i < N; i++)
{
p[i] = i;
} CNormclass* pn = new CNormclass[N];
CTestPointerType*po = new CTestPointerType[N]; const int*pc = &N;
CHeapDebugger<CNormclass, N>::Print(pn); delete po; }
#pragma endregion
#pragma endregion #pragma region 右值引用和MOVE
void TestRef(){
int a = , b = ;
int& ra = a;
cout << ra << endl; //
ra = b; //此时ra不是a的引用也不是b的引用,而是一个普通变量
b = ;
cout << ra << endl; // }
#pragma endregion
#pragma region C11智能指针 #pragma endregion
#pragma region 正则表达式 #pragma endregion
#pragma region lambda表达式 #pragma endregion
#pragma region unorder_map及hashtable实现
//有没有无冲突哈希算法 #pragma endregion
#pragma region DIJKASTRA最短路径算法 class Obj {
public:
Obj(float fx) {
x = fx;
}
float x;
};
bool cmpfunc(Obj a, Obj b) {
return a.x < b.x;
} void TestStlSortFunc() {
std::vector<Obj> vec;
vec.push_back(Obj());
vec.push_back(Obj());
vec.push_back(Obj(1.3f));
vec.push_back(Obj(2.31));
vec.push_back(Obj());
vec.push_back(Obj());
vec.push_back(Obj()); int ax = ;
auto iter = max_element(vec.begin(), vec.end(), [ax](Obj obj1, Obj obj2){
cout << "cap addr of ax : " << ax << endl;
return obj1.x < obj2.x;
});
cout << (*iter).x << endl;
} void RemoveVecElem(std::vector<int>& v, int e) {
for (auto it = v.begin(); it != v.end();) {
if (*it == e)
{
it = v.erase(it);
break;
}
else
it++;
}
}
void Dijkastra() {
const int m = ;
const int n = m;
const int nodeCount = ; int paths[][nodeCount] = {
{ n, , , m, , m, m },
{ m, n, m, m, , m, m },
{ m, , n, , m , m, m },
{ m, m, m, n, m , m, m },
{ m, m, m, , n , m, m },
{ m, m, m, m, , n, },
{ m, , m, m, m , m, n },
}; std::vector<string> sel;
std::vector<int> left{ , , , , , , };
sel.reserve();
left.reserve(); int startIdx;
cout << ">> 选择一个起点 " << endl;
cin >> startIdx;
cout << ">> v" << startIdx << endl; if (startIdx >= nodeCount)
return; RemoveVecElem(left, startIdx);
cout << "after erase : " << left.capacity() << endl;
for (auto e:left)
{
cout << e << ",";
}
cout << endl; cout << ">> calculating ..." << endl;
int tmp[nodeCount];
for (int i = ; i < nodeCount; ++i) {
tmp[i] = paths[startIdx][i];
} std::stringstream ss;
//ss >> "v" >> startIdx; auto iter = min_element(tmp, tmp + nodeCount);
cout << *iter << "," << iter - tmp << endl; int curMinNode = iter - tmp;
int curMinPathLen = *iter;
// ss >> "->v" >> curMinNode;
//sel.push_back(ss.str());
//ss.clear();
RemoveVecElem(left, curMinNode); while (left.size() > ) {
bool isfind = false;
for (int i = ; i < nodeCount; ++i) {
int p1 = paths[startIdx][i];
for (int j = ; j < nodeCount; ++j) {
bool isold = false;
for (int i = ; i < left.size(); ++i) {
if (left[i] == j)
isold = true;
}
if (!isold) {
int p2 = paths[curMinNode][j];
if (j != curMinNode) { //j != curMinNode
if ((curMinPathLen + p2) < p1) {
isfind = true;
paths[startIdx][i] = (curMinPathLen + p2);
}
}
}
}
} if (left.size() == )break; auto p = paths[startIdx];
auto iter2 = std::min_element(left.begin(), left.end());
curMinPathLen = *iter2;
//curMinNode = iter2 - left.be;
RemoveVecElem(left, curMinNode);
cout << "left: " << left.size() << endl;
} // sel.push_back(0);
// sel.erase(sel.begin());
// sel.shrink_to_fit();
// cout << "cap: " << sel.capacity() << endl;
// for (int d : sel)
// {
// cout << d << endl;
// }
// cout << sel.size() << endl;
}
#pragma endregion
#pragma region EffectiveC++
namespace EffectiveCpp {
#pragma endregion x
#pragma region 02-以const,enum,inline替代define
class CStaticConst {
public:
//【1】,static const 可以同时存在,这在C#中是不允许的
//在C#中,常量也是属于类而不属于对象,这就等价于C++的 static cosnt 合体了
static const float fx; //【声明式】 //【2】,浮点类型,不能在定义时初始化
//static float fx2 = 3; //【错误】 //【3】,整数类型(整形,char,枚举),可以在定义时初始化,且不需要在类外写定义式
static const int ix = ; //声明并初始化,注意,这不是定义,也就是说声明时可以赋值 enum {NumTurns = };
int scores[NumTurns]; //enum hack //【不安全宏的替代品】,既有宏的高效率和函数的安全性
template<typename T>
inline T safe_max(const T& a, const T& b) {
return a > b ? a : b;
}
};
const float CStaticConst::fx = ; //【定义式】:不能写static
//const int CStaticConst::ix = 3; //【错误】,已经初始化过了,不能重复
const int CStaticConst::ix; //定义式,声明时已初始化了。因为是整数类型,这个定义式可以不写 //1,【宏是不安全的】任何时候都不要忘了给宏的实参加上()
//2 替代方法:使用 template inline
#define unsave_max(a, b) (a) > (b) ? (a) : (b) void Test02() {
CStaticConst oc;
cout << oc.fx << endl;
int a(), b(); //不安全的宏,下面这样的导致b被加两次
max(++a, b++);
cout << "a=" << a << ", b=" << b << endl;
}
#pragma endregion } #pragma endregion #pragma endregion
const int* TestConstarr() {
int* iarr = new int[]{ , , };
return iarr;
}
int _tmain(int argc, _TCHAR* argv[])
{
EffectiveCpp::Test02();
//TestStlSortFunc();
//Dijkastra();
//TestPointerType();
//TestSameTypeAssign();
//TestRef();
//TestTehuaTemp();
//TestCComplexOper();
//TestTypecastOper();
//TestTypecastContructor();
//TestNestingFuncPtrs();
//TestArrayAndPointer();
///TestRealloc();
//TestComputeDataStorage();
//TestVirtualFunctionTable();
//TestAdd2Num();
//TestAstrPstr();
//TestCWithStatic();
//TestThread();
//TestVarTemplate(); const int arr[] = { , , };
int a1[], a2[];
TestConstarr();
return ;
}

C++全总结的更多相关文章

  1. Jquery的点击事件,三句代码完成全选事件

    先来看一下Js和Jquery的点击事件 举两个简单的例子 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&q ...

  2. Android UI体验之全屏沉浸式透明状态栏效果

    前言: Android 4.4之后谷歌提供了沉浸式全屏体验, 在沉浸式全屏模式下, 状态栏. 虚拟按键动态隐藏, 应用可以使用完整的屏幕空间, 按照 Google 的说法, 给用户一种 身临其境 的体 ...

  3. 通往全栈工程师的捷径 —— react

    腾讯Bugly特约作者: 左明 首先,我们来看看 React 在世界范围的热度趋势,下图是关键词“房价”和 “React” 在 Google Trends 上的搜索量对比,蓝色的是 React,红色的 ...

  4. 工欲善其事,必先利其器 之 VS2013全攻略(安装,技巧,快捷键,插件)!

    如有需要WPF工具的朋友可以移步 工欲善其事,必先利其器 之 WPF篇: 随着开发轨迹来看高效WPF开发的工具和技巧 之前一篇<c++的性能, c#的产能?!鱼和熊掌可以兼得,.NET NATI ...

  5. 【C#代码实战】群蚁算法理论与实践全攻略——旅行商等路径优化问题的新方法

    若干年前读研的时候,学院有一个教授,专门做群蚁算法的,很厉害,偶尔了解了一点点.感觉也是生物智能的一个体现,和遗传算法.神经网络有异曲同工之妙.只不过当时没有实际需求学习,所以没去研究.最近有一个这样 ...

  6. Phoenix综述(史上最全Phoenix中文文档)

    个人主页:http://www.linbingdong.com 简书地址:http://www.jianshu.com/users/6cb45a00b49c/latest_articles 网上关于P ...

  7. duang~免费的学习视频来啦:学霸君之全栈测试

    学霸君向童鞋们推荐一款 同名学霸学习 视频教程 重点是完全免费收看学习噢!!! 今天 学霸君推荐腾讯课堂的学霸君之全栈测试 复制下方链接至腾讯课堂中报名学习 https://ke.qq.com/cou ...

  8. 【C#公共帮助类】 Utils 10年代码,最全的系统帮助类

    为大家分享一下个人的一个Utils系统帮助类,可能有些现在有新的技术替代,自行修改哈~ 这个帮助类主要包含:对象转换处理 .分割字符串.截取字符串.删除最后结尾的一个逗号. 删除最后结尾的指定字符后的 ...

  9. H3 BPM:为石化企业提供一个不一样的全停大修平台

    H3 BPM大型炼化企业装置全停检修管理平台(简称"全停大修")结合国际化的流程管理理念.成熟的系统技术架构.优秀的行业解决方案,为石油化工行业全停大修提供了卓越的信息化管理方案, ...

  10. 超全面的.NET GDI+图形图像编程教程

    本篇主题内容是.NET GDI+图形图像编程系列的教程,不要被这个滚动条吓到,为了查找方便,我没有分开写,上面加了目录了,而且很多都是源码和图片~ (*^_^*) 本人也为了学习深刻,另一方面也是为了 ...

随机推荐

  1. L175 Endorestiform Nucleus: Scientist Just Discovered a New Part of the Human Brain

    The newly named Endorestiform Nucleus sits in the inferior cerebellar小脑 peduncle, at the junction be ...

  2. node 应用集合

    node+react上传 淘宝的formidable express部署

  3. python中多线程

    多线程 什么是多线程 开启线程的两种方式 进程和线程的区别 Thread对象的其他属性和方法 守护线程 死锁现象与递归锁 信号量.Event定时器 线程Queue 进程池和线程池 什么是多线程 在传统 ...

  4. I.MX6 Manufacturing Tool V2 (MFGTool2) Update Command List (UCL) User Guide translate

    Manufacturing Tool V2 (MFGTool2) Update Command List (UCL) User Guide Contents(目录) Contents(目录)     ...

  5. CF1082D:Maximum Diameter Graph (简单构造)

    Graph constructive problems are back! This time the graph you are asked to build should match the fo ...

  6. CF1143D/1142A The Beatles

    CF1143D/1142A The Beatles 将题目中所给条件用同余方程表示,可得 \(s-1\equiv \pm a,s+l-1\equiv \pm b\mod k\). 于是可得 \(l\e ...

  7. python list 中元素的统计与排序

    1.  用count和dict.  dict的存储是散乱的, 不方面打印. 2. 用sorted.  注意, 得到的是一个元组list, 而不再是dict. dict_x = {} for item ...

  8. nats 学习 集群ha 配置

      nats 的ha 是一个mesh 的结构,有两个主要的参数 clusters routers 启动三分节点(单机) 共享变量 SERVERS=nats://127.0.0.1:6222,nats: ...

  9. FastAdmin 升级后出现 is already in use

    FastAdmin 升级后出现 is already in use 升级 FastAdmin 改进很多,但全新安装出现以下错误 Cannot use app\common\library\Menu a ...

  10. excel 条件格式 心的

    例1: 图1 图2 $G$16 ≠G16  用G16就可以用格式刷拖动,$G$16用格式刷刷到其它单元格保持不变,判断单元格函数 ISBLANK(G16)=TRUE