晴天霹雳你觉得很惊讶吗?
哈哈哈,我可是天天都看到各种霹雳!早已习空见惯了。
从大学出来就由于工作原因一直没再使用C++。(大学的时候,我们老师可是极力推荐C#)
工作、规划需要,是时候重温回来了。
C++其实也是很简单的,每个部分都很简单,但是因为语言特性非常的多,需要时间熟悉与记忆。
那就从最开始的 hello world 出发。
#include
int main() {
std::cout<<"hello world!"<<std::endl;
return 0;
}
CMD | Des |
---|---|
-ansi | 只支持 ANSI 标准的 C 语法。这一选项将禁止 GNU C 的某些特色, 例如 asm 或 typeof 关键词。 |
-c | 只编译并生成目标文件。 |
-DMACRO | 以字符串"1"定义 MACRO 宏。 |
-DMACRO=DEFN | 以字符串"DEFN"定义 MACRO 宏。 |
-E | 只运行 C 预编译器。 |
-g | 生成调试信息。GNU 调试器可利用该信息。 |
-IDIRECTORY | 指定额外的头文件搜索路径DIRECTORY。 |
-LDIRECTORY | 指定额外的函数库搜索路径DIRECTORY。 |
-lLIBRARY | 连接时搜索指定的函数库LIBRARY。 |
-m486 | 针对 486 进行代码优化。 |
-o | FILE 生成指定的输出文件。用在生成可执行文件时。 |
-O0 | 不进行优化处理。 |
-O | 或 -O1 优化生成代码。 |
-O2 | 进一步优化。 |
-O3 | 比 -O2 更进一步优化,包括 inline 函数。 |
-shared | 生成共享目标文件。通常用在建立共享库时。 |
-static | 禁止使用共享连接。 |
-UMACRO | 取消对 MACRO 宏的定义。 |
-w | 不生成任何警告信息。 |
-Wall | 生成所有警告信息。 |
MSVC是微软的VS对C++默认的编译器
按字母顺序列出的编译器选项
Compiler optimizations
/* a.cpp - jave.lin 2020.04.30 */
#include
#include
#define DEF_IN_FILE
int main() {
std::cout<<"hello world\n";
// ===== 外部编译参数定义的宏 start =====
#ifdef Debug // 注意区别大小写
std::cout<<"this is debugging message.\n";
#endif
#ifdef MY_MACRO // 注意区别大小写
std::cout<<"this is my_macro message.\n";
#endif
// ===== 外部编译参数定义的宏 end =====
// ===== 源文件内定义的宏 start =====
#ifdef DEF_IN_FILE
std::cout<<"this is define macro in file message."<<std::endl;
#endif
// ===== 源文件内定义的宏 end =====
printf("input string please:\n");
std::string inputStr;
std::cin>>inputStr;
std::cout<<"this is your input msg:"<<inputStr<<std::endl;
return 0;
}
@rem compile.bat - jave.lin 2020.04.30
g++ a.cpp -o out -DDebug -DMY_MACRO
.\out.exe
@pause
D:\jave\Work Files\CPP\hello_world>g++ a.cpp -o out -DDebug -DMY_MACRO
D:\jave\Work Files\CPP\hello_world>.\out.exe
hello world
this is debugging message.
this is my_macro message.
this is define macro in file message.
input string please:
GodLike
this is your input msg:GodLike
请按任意键继续. . .
这里虽然用了最简单的hello world演示,但需要注意我们使用宏分支,与定义宏的位置
其中定义宏,目前例子表示了两种:
#define
-D
/* a.cpp - jave.lin 2020.04.30 */
#include
#define A
#define B
int main() {
#ifdef A
printf("defined A.\n");
#endif
#ifdef B
printf("defined B.\n");
#endif
#ifdef C
printf("defined C.\n");
#endif
#ifdef D
printf("defined D.\n");
#endif
#ifdef E
printf("defined E.\n");
#endif
#if TAG == 1
printf("tag == 1.\n");
#elif TAG == 2
printf("tag == 2.\n");
#elif TAG == 2
printf("tag == 3.\n");
#endif
return 0;
}
@rem compile.bat - jave.lin 2020.04.30
@rem -U 只能对命令行 -D 的宏定义取消,源文件内已定义的取消不了
@rem 可以看到 -UA 取消宏定义了,但是还是会输出 A 宏定义的内容
@rem 也可以看到先 -DD, 再 -UD,就可以取消了-D 命令行的宏定义
@rem 还可以看到先 -UE 一个没有定义过的 E,再重新-DE,结果还是会输出 E 的内容,所以-D -U之间的顺序很重要
g++ a.cpp -o out -DC -DD -UD -UA -UE -DE -DTAG=2
.\out.exe
@pause
D:\jave\Work Files\CPP\-UMacro>g++ a.cpp -o out -DC -DD -UD -UA -UE -DE -DTAG=2
D:\jave\Work Files\CPP\-UMacro>.\out.exe
defined A.
defined B.
defined C.
defined E.
tag == 2.
C/C++中#pragma once的使用
C/C++中为了避免同一个文件被多次include的问题通常做法是:
#ifndef __XXX_H__
#define __XXX_H__
// file content
#endif
但是这种方法有弊端,如果大型项目中,碰巧__XXX_H__
定义在与其他文件的宏定义重名,那就会有问题了
而 #pragma once
就不会有这问题。
#pragma once
只要放在需要被include文件的第一行即可
它底层应该是以文件名或是MD5,或是GUID,之类的来处理的。
听说是比上面那种#ifndef
的方法的编译速度更快。所以推荐使用
但是部分低版本的编译器不支持。gcc/g++ 3.4以上才能用。
Preprocessor directives
/* a.cpp - jave.lin 2020.04.30 */
#include
int main() {
// NULL same as cpp's nullptr
int* p = NULL;
int* q = nullptr;
printf("p:%p, q:%p\n", p, q);
return 0;
}
p:00000000, q:00000000
/* b.h jave.lin*/
class Display{};
/* a.cpp - jave.lin */
#include
#include
#include "b.h"
class A {
};
class B {
};
typedef struct C {
} C;
class Human{};
class Person : public Human {};
int main() {
// NULL same as cpp's nullptr
int* p = NULL;
int* q = nullptr;
int P, Q;
A* a = NULL;
B b;
C c;
B* B;
C* C;
Display d;
Display* D;
Human h;
Human* H = new Human();
Person ps;
Person* PS = new Person();
int arr[9];
int arr2[9][9];
int arr3[9][9][9];
int arr4[9][9][9][9];
printf("p:%s, q:%s, P:%s, Q:%s, a:%s, b:%s, c:%s, B:%s, C:%s, d:%s, D:%s\n",
typeid(p).name(),
typeid(q).name(),
typeid(P).name(),
typeid(Q).name(),
typeid(a).name(),
typeid(b).name(),
typeid(c).name(),
typeid(B).name(),
typeid(C).name(),
typeid(d).name(),
typeid(D).name()
);
printf("h:%s, H:%s, ps:%s, PS:%s\n",
typeid(h).name(),
typeid(H).name(),
typeid(ps).name(),
typeid(PS).name()
);
printf("arr:%s, arr2:%s, arr3:%s, arr4:%s\n",
typeid(arr).name(),
typeid(arr2).name(),
typeid(arr3).name(),
typeid(arr4).name()
);
return 0;
}
/*
输出:
p:Pi, q:Pi, P:i, Q:i, a:P1A, b:1B, c:1C, B:P1B, C:P1C, d:7Display, D:P7Display
h:5Human, H:P5Human, ps:6Person, PS:P6Person
arr:A9_i, arr2:A9_A9_i, arr3:A9_A9_A9_i, arr4:A9_A9_A9_A9_i
*/
可查看可执行文件的数据结构
PS D:\jave\Work Files\CPP\typeid> size .\out.exe
text data bss dec hex filename
16708 2024 116 18848 49a0 .\out.exe
C/C++中的输出对齐设置
std::cout.setf(std::ios::left) // 默认右对齐,可设置为左对齐
std::cout.width(int i) // 设置后续每次cout<<输出占位几个字符,不足个数将不空格
参考:
// jave.lin - 调整Console输出字符颜色
#include "stdio.h"
#include "windows.h"
#define _FR FOREGROUND_RED
#define _FG FOREGROUND_GREEN
#define _FB FOREGROUND_GREEN
#define _FI FOREGROUND_INTENSITY
#define MY_TEXT_COLOR _FR | _FG | _FB
#define MY_TEXT_COLOR_INTENSITY _FR | _FG | _FB | _FI
int main() {
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO srcConsoleScreenBuffInfo;
BOOL successfully = GetConsoleScreenBufferInfo(handle, &srcConsoleScreenBuffInfo);
SetConsoleTextAttribute(handle, _FG);
printf("Hello");
SetConsoleTextAttribute(handle, _FR);
printf(" World!\n");
SetConsoleTextAttribute(handle, MY_TEXT_COLOR);
printf("My Message 1!\n");
SetConsoleTextAttribute(handle, MY_TEXT_COLOR_INTENSITY);
printf("My Message 2!\n");
if (successfully) {
SetConsoleTextAttribute(handle, srcConsoleScreenBuffInfo.wAttributes);
}
printf("Default color.\n");
return 0;
}
// jave.lin
#include
#include
#define _FR FOREGROUND_RED
#define _FG FOREGROUND_GREEN
#define _FB FOREGROUND_BLUE
template<typename... Args>
void printf_col(short col, const char* format, Args... args) {
HANDLE handle = GetStdHandle(STD_OUTPUT_HANDLE);
CONSOLE_SCREEN_BUFFER_INFO srcConsoleScreenBuffInfo;
BOOL successfully = GetConsoleScreenBufferInfo(handle, &srcConsoleScreenBuffInfo);
SetConsoleTextAttribute(handle, col | FOREGROUND_INTENSITY);
printf(format, args...);
if (successfully) {
SetConsoleTextAttribute(handle, srcConsoleScreenBuffInfo.wAttributes);
}
}
template<typename... Args>
void printf_col_red(const char* format, Args... args) {
printf_col(_FR, format, args...);
}
template<typename... Args>
void printf_col_green(const char* format, Args... args) {
printf_col(_FG, format, args...);
}
template<typename... Args>
void printf_col_blue(const char* format, Args... args) {
printf_col(_FB, format, args...);
}
int main() {
printf("=== testing set console color text start ===\n");
printf_col_red("this is red num : %d\n", 123);
printf_col_green("this is red num : %d\n", 123);
printf_col_blue("this is red num : %d\n", 123);
printf("this is RGB num : ");
printf_col_red("%d ", 123);
printf_col_green("%d ", 456);
printf_col_blue("%d\n", 789);
printf("=== testing set console color text end ===\n");
return 0;
}
/* a.cpp - jave.lin */
#include
#include
#include
#include
#define PRINTF_NUM_INFO(type,min_format,max_format)\
printf("%15s\tsize:%2d""\tmin:"#min_format"\tmax:"#max_format"\n",\
#type,\
sizeof(type),\
std::numeric_limits::min(),\
std::numeric_limits::max()\
);
#define COUT_NUM_INFO(type)\
std::cout.width(15);\
std::cout<<#type;\
std::cout<<"\tsize:";\
std::cout.width(2);\
std::cout<::min();\
std::cout<<"\tmax:";\
std::cout.width(20);\
std::cout<::max()<<"\n";
void print_method() {
printf("printf=====type\tsize:00\tmin:00000000000000000000\tmax:00000000000000000000=====\n", "type");
PRINTF_NUM_INFO(bool,%20d,%20d)
PRINTF_NUM_INFO(char,%20d,%20d)
PRINTF_NUM_INFO(signed char,%20d,%20d)
PRINTF_NUM_INFO(unsigned char,%20d,%20d)
PRINTF_NUM_INFO(wchar_t,%20d,%20d)
PRINTF_NUM_INFO(short,%20d,%20d)
PRINTF_NUM_INFO(unsigned short,%20d,%20d)
PRINTF_NUM_INFO(int,%20d,%20d)
PRINTF_NUM_INFO(unsigned int,%20d,%20d)
PRINTF_NUM_INFO(unsigned,%20d,%20d)
PRINTF_NUM_INFO(long,%20d,%20d)
PRINTF_NUM_INFO(unsigned long,%20d,%20d)
PRINTF_NUM_INFO(long int,%20ld,%20ld)
PRINTF_NUM_INFO(long long,%20ld,%20ld)
PRINTF_NUM_INFO(double,%20e,%20e)
PRINTF_NUM_INFO(long double,%20llg,%20llg)
PRINTF_NUM_INFO(float,%20e,%20e)
PRINTF_NUM_INFO(size_t,%20d,%20d)
PRINTF_NUM_INFO(std::string,%20d,%20d)
printf("printf=====type\tsize:00\tmin:00000000000000000000\tmax:00000000000000000000=====\n", "type");
}
void cout_method() {
printf("cout=======type\tsize:00\tmin:00000000000000000000\tmax:00000000000000000000=====\n", "type");
COUT_NUM_INFO(bool)
COUT_NUM_INFO(char)
COUT_NUM_INFO(signed char)
COUT_NUM_INFO(unsigned char)
COUT_NUM_INFO(wchar_t)
COUT_NUM_INFO(short)
COUT_NUM_INFO(unsigned short)
COUT_NUM_INFO(int)
COUT_NUM_INFO(unsigned int)
COUT_NUM_INFO(unsigned)
COUT_NUM_INFO(long)
COUT_NUM_INFO(unsigned long)
COUT_NUM_INFO(long int)
COUT_NUM_INFO(long long)
COUT_NUM_INFO(double)
COUT_NUM_INFO(long double)
COUT_NUM_INFO(float)
COUT_NUM_INFO(size_t)
COUT_NUM_INFO(std::string)
printf("cout=======type\tsize:00\tmin:00000000000000000000\tmax:00000000000000000000=====\n", "type");
}
int main() {
print_method();
printf("\n\n");
cout_method();
return 0;
}
printf=====type size:00 min:00000000000000000000 max:00000000000000000000=====
bool size: 1 min: 0 max: 1
char size: 1 min: -128 max: 127
signed char size: 1 min: -128 max: 127
unsigned char size: 1 min: 0 max: 255
wchar_t size: 2 min: 0 max: 65535
short size: 2 min: -32768 max: 32767
unsigned short size: 2 min: 0 max: 65535
int size: 4 min: -2147483648 max: 2147483647
unsigned int size: 4 min: 0 max: -1
unsigned size: 4 min: 0 max: -1
long size: 4 min: -2147483648 max: 2147483647
unsigned long size: 4 min: 0 max: -1
long int size: 4 min: -2147483648 max: 2147483647
long long size: 8 min: 0 max: -2147483648
double size: 8 min: 2.225074e-308 max: 1.797693e+308
long double size:12 min: -0 max: -1.#QNAN
float size: 4 min: 1.175494e-038 max: 3.402823e+038
size_t size: 4 min: 0 max: -1
std::string size:24 min: 6422232 max: 6422208
printf=====type size:00 min:00000000000000000000 max:00000000000000000000=====
cout=======type size:00 min:00000000000000000000 max:00000000000000000000=====
bool size: 1 min: 0 max: 1
char size: 1 min: € max:
signed char size: 1 min: € max:
unsigned char size: 1 min: max:
wchar_t size: 2 min: 0 max: 65535
short size: 2 min: -32768 max: 32767
unsigned short size: 2 min: 0 max: 65535
int size: 4 min: -2147483648 max: 2147483647
unsigned int size: 4 min: 0 max: 4294967295
unsigned size: 4 min: 0 max: 4294967295
long size: 4 min: -2147483648 max: 2147483647
unsigned long size: 4 min: 0 max: 4294967295
long int size: 4 min: -2147483648 max: 2147483647
long long size: 8 min:-9223372036854775808 max: 9223372036854775807
double size: 8 min: 2.22507e-308 max: 1.79769e+308
long double size:12 min: 3.3621e-4932 max: 1.18973e+4932
float size: 4 min: 1.17549e-038 max: 3.40282e+038
size_t size: 4 min: 0 max: 4294967295
std::string size:24 min: max:
cout=======type size:00 min:00000000000000000000 max:00000000000000000000=====
c++里的与c的extern一样功能。可以声明外部声明、定义的成员。
还可以用于c++混编时指示编译器在 extern “C” { … }之间的按C风格编译
// jave.lin - 测试类型 2d array[][] 转 pointer
#include
typedef float vec4[4];
typedef vec4 mat4x4[4];
typedef float GLfloat;
int main() {
printf(" === printf vec4 ===\n");
vec4 pos = {1,2,3,4};
for (size_t i = 0; i < 4; i++) {
printf("pos[%d]=%.2f,", i, pos[i]);
}
printf("\n\n");
printf(" === printf mat4x4 ===\n");
mat4x4 mat = {
{ 1, 2, 3, 4},
{ 5, 6, 7, 8},
{ 9,10,11,12},
{13,14,15,16},
};
for (size_t i = 0; i < 4; i++) {
for (size_t j = 0; j < 4; j++) {
printf("pos[%d][%d]=%.2f,", i, j, mat[i][j]);
}
printf("\n");
}
printf("\n");
printf(" === printf GLfloat* ===\n");
GLfloat *pFloat = (GLfloat*) mat;
for (size_t i = 0; i < 16; i++) {
printf("*(pos+%d)=%.2f,", i, *(pFloat + i));
if ((i + 1) % 4 == 0) printf("\n");
}
printf("\n");
return 0;
}
/* 输出:
=== printf vec4 ===
pos[0]=1.00,pos[1]=2.00,pos[2]=3.00,pos[3]=4.00,
=== printf mat4x4 ===
pos[0][0]=1.00,pos[0][1]=2.00,pos[0][2]=3.00,pos[0][3]=4.00,
pos[1][0]=5.00,pos[1][1]=6.00,pos[1][2]=7.00,pos[1][3]=8.00,
pos[2][0]=9.00,pos[2][1]=10.00,pos[2][2]=11.00,pos[2][3]=12.00,
pos[3][0]=13.00,pos[3][1]=14.00,pos[3][2]=15.00,pos[3][3]=16.00,
=== printf GLfloat* ===
*(pos+0)=1.00,*(pos+1)=2.00,*(pos+2)=3.00,*(pos+3)=4.00,
*(pos+4)=5.00,*(pos+5)=6.00,*(pos+6)=7.00,*(pos+7)=8.00,
*(pos+8)=9.00,*(pos+9)=10.00,*(pos+10)=11.00,*(pos+11)=12.00,
*(pos+12)=13.00,*(pos+13)=14.00,*(pos+14)=15.00,*(pos+15)=16.00,
*/
struct my_vec3;
//using test_vec3 = vec3
using test_vec3 = struct my_vec3;
struct my_vec3 {
float x, y, z;
my_vec3() {
memset((void*)this, 0, sizeof(my_vec3));
}
my_vec3(float x, float y, float z) {
this->x = x;
this->y = y;
this->z = z;
}
};
int main() {
test_vec3* vertices = new test_vec3[3];
for (size_t i = 0; i < 3; i++) {
vertices[i] = test_vec3(i * 3 + 0, i * 3 + 1, i * 3 + 2);
}
std::cout << "floats : ";
float* floats = (float*)vertices;
for (size_t i = 0; i < 9; i++) {
std::cout << *(floats + i);
}
std::cout << "\n";
return 0;
}
// 输出:
// floats : 012345678
指针使用比较简单,但是因为太灵活了,有些时候很难做到不泄露内容。
所以新版C++ 也做了引用计数管理的 智能指针
C++智能指针和普通指针转换需要注意的问题
C++笔记-shared_ptr与weak_ptr需要注意的地方
#include
#include
#define P(str,format) printf(#str ":" #format "\n", (str));
int main() {
std::string str = "This is an Message!";
std::string str1 = "hello world!";
P(str.data(), %s)
P(str1.data(), %s)
printf("=== string properties: ===\n");
P(str.size(), %d)
P(str.substr(1,2).c_str(), %s)
P(str.substr(1,-1).c_str(), %s)
P(str.capacity(), %d)
P(str[2], %c)
P(str.at(3), %c)
P(str.data(), %s)
P(str.c_str(), %s)
P(str.empty(), %d)
P(str.find('i'), %d)
P(str.find('a'), %d)
P(str.find('A'), %d)
P(str.find_first_not_of('a'), %d)
P(str.find_first_of('a'), %d)
P(str.find_last_not_of('e'), %d)
P(str.find_last_not_of('a'), %d)
P(str.find_last_of('a'), %d)
P(str.find_last_of('e'), %d)
P(str.length(), %d)
P(str.max_size(), %d)
P(str.npos, %d)
P(str.back(), %c)
printf("\n=== after string.swap(str1) properties ===\n");
printf(" --- before ---\n");
P(str.data(), %s)
P(str1.data(), %s)
str.swap(str1);
printf(" --- after ---\n");
P(str.data(), %s)
P(str1.data(), %s)
printf("\n=== after str1.reserve() properties ===\n");
printf(" --- before ---\n");
P(str1.data(), %s)
str1.reserve();
printf(" --- after ---\n");
P(str1.data(), %s)
printf("\n=== after str.pop_back() & str.push_back('$') ===\n");
printf(" --- before str.pop_back() ---\n");
P(str.data(), %s)
str.pop_back();
printf(" --- after str.pop_back() ---\n");
P(str.data(), %s)
printf(" --- before push_back('$') ---\n");
P(str.data(), %s)
str.push_back('$');
printf(" --- after push_back('$') ---\n");
P(str.data(), %s)
printf("\n=== str.copy(copy_str, 2, 1),");
char copy_str[20];
str.copy(copy_str, 2, 1);
copy_str[2] = '\0';
P(copy_str, %s)
printf("\n=== str.ease(1) ===\n");
P(str.erase(1).data(), %s)
printf("\n=== after string.clear properties ===\n");
str.clear();
P(str.empty(), %d)
P(str.size(), %d)
P(str.capacity(), %d)
P(str.length(), %d)
printf("\n=== after string.shrink_to_fit properies ===\n");
str.shrink_to_fit();
P(str.empty(), %d)
P(str.size(), %d)
P(str.capacity(), %d)
P(str.length(), %d)
printf("\n=== after string.insert properites ===\n");
printf("str.insert(str.begin(), 'e':");
printf("str.insert(str.begin(), 'a':");
printf("str.insert(str.begin(), 't':");
std::string::iterator it = str.begin();
str.insert(it, 'e');
str.insert(it, 'a');
str.insert(it, 't');
P(str.data(), %s)
str.end();
printf("\n=== string.resize properites ===\n");
P(str.data(), %s)
str.resize(1);
P(str.data(), %s)
printf("\n=== string.append('123') properites ===\n");
str.append("123");
P(str.data(), %s)
return 0;
}
str.data():This is an Message!
str1.data():hello world!
=== string properties: ===
str.size():19
str.substr(1,2).c_str():hi
str.substr(1,-1).c_str():his is an Message!
str.capacity():19
str[2]:i
str.at(3):s
str.data():This is an Message!
str.c_str():This is an Message!
str.empty():0
str.find('i'):2
str.find('a'):8
str.find('A'):-1
str.find_first_not_of('a'):0
str.find_first_of('a'):8
str.find_last_not_of('e'):18
str.find_last_not_of('a'):18
str.find_last_of('a'):15
str.find_last_of('e'):17
str.length():19
str.max_size():1073741823
str.npos:-1
str.back():!
=== after string.swap(str1) properties ===
--- before ---
str.data():This is an Message!
str1.data():hello world!
--- after ---
str.data():hello world!
str1.data():This is an Message!
=== after str1.reserve() properties ===
--- before ---
str1.data():This is an Message!
--- after ---
str1.data():This is an Message!
=== after str.pop_back() & str.push_back('$') ===
--- before str.pop_back() ---
str.data():hello world!
--- after str.pop_back() ---
str.data():hello world
--- before push_back('$') ---
str.data():hello world
--- after push_back('$') ---
str.data():hello world$
=== str.copy(copy_str, 2, 1),copy_str:el
=== str.ease(1) ===
str.erase(1).data():h
=== after string.clear properties ===
str.empty():1
str.size():0
str.capacity():15
str.length():0
=== after string.shrink_to_fit properies ===
str.empty():1
str.size():0
str.capacity():15
str.length():0
=== after string.insert properites ===
str.insert(str.begin(), 'e':str.insert(str.begin(), 'a':str.insert(str.begin(), 't':str.data():tae
=== string.resize properites ===
str.data():tae
str.data():t
=== string.append('123') properites ===
str.data():t123
// jave.lin - 测试C++的字符串常量的多种定义方式
#include
int main() {
const char* str1 =
"this is str1 content : \n blablablablabla!!!\n";
const char* str2 =
"title : this is str2 content:\n"
" second line content : hello world!\n"
" third line content : Your are the hero!\n";
const char* str3 = R"str3(
this is str3 string content :
there are definition which writing the raw-string.
real handway!
)str3";
const char* str4 = R"str3(
also flag equal the : str3, test to compile.
)str3";
const char* str5 = R"str3(
111111111111111
)str3";
const char* str6 = R"str3(222222222222)str3";
std::cout << "str1 : " << str1 << "\n";
std::cout << "str2 : " << str2 << "\n";
std::cout << "str3 : " << str3 << "\n";
std::cout << "str4 : " << str4 << "\n";
std::cout << "str5 : " << str5 << "\n";
std::cout << "str6 : " << str6 << "\n";
return 0;
}
/*
str1 : this is str1 content :
blablablablabla!!!
str2 : title : this is str2 content:
second line content : hello world!
third line content : Your are the hero!
str3 :
this is str3 string content :
there are definition which writing the raw-string.
real handway!
str4 :
also flag equal the : str3, test to compile.
str5 :
111111111111111
str6 : 222222222222
*/
C++stringstream的妙用
// jave.lin
#include
#include
void print_chars(const char* chars) {
while(*chars != 0) {
std::cout << *chars << "(" << (int)(*chars) << "),";
chars++;
}
std::cout << "\n";
}
void print_chars(size_t count, const char* chars, const int* len) {
size_t offset = 0;
for (size_t i = 0; i < count; i++) {
int the_len = *(len + i);
if (the_len < 0) {
while(*(chars + offset) != 0) {
std::cout << *(chars + offset) << "(" << (int)(*(chars + offset)) << "),";
offset++;
}
std::cout << "\n";
offset++;
} else {
int j = 0;
while(j < the_len) {
std::cout << *(chars + offset) << "(" << (int)(*(chars + offset)) << "),";
offset++;
j++;
}
std::cout << "\n";
}
}
}
void print_str(std::string str) {
for (size_t i = 0; i < str.size(); i++) {
std::cout << str[i] << "(" << (int)(str[i]) << "),";
}
std::cout << "\n";
}
int main() {
std::cout << "=== print_chars(chars) ===\n";
const char* chars =
"first"
"second"
"thrid";
print_chars(chars);
std::cout << "\n=== print_str(chars) ===\n";
print_str(chars);
std::cout << "\n=== print_chars(3, chars1, lens) ===\n";
const char* chars1 =
"first\0"
"second\0"
"thrid\0";
int lens[3] = {-1,-1,-1};
print_chars(3, chars1, lens);
std::cout << "\n=== print_chars(3, chars2, lens1) ===\n";
const char* chars2 =
"first"
"second"
"thrid";
int lens1[3] = {5,6,5};
print_chars(3, chars2, lens1);
std::cout << "\n=== print_chars(3, chars3, lens2) ===\n";
const char* chars3 =
"first\0"
"second\0\0"
"thrid\0\0\0";
int lens2[3] = {6,8,8};
print_chars(3, chars3, lens2);
return 0;
}
/* 输出:
=== print_chars(chars) ===
f(102),i(105),r(114),s(115),t(116),s(115),e(101),c(99),o(111),n(110),d(100),t(116),h(104),r(114),i(105),d(100),
=== print_str(chars) ===
f(102),i(105),r(114),s(115),t(116),s(115),e(101),c(99),o(111),n(110),d(100),t(116),h(104),r(114),i(105),d(100),
=== print_chars(3, chars1, lens) ===
f(102),i(105),r(114),s(115),t(116),
s(115),e(101),c(99),o(111),n(110),d(100),
t(116),h(104),r(114),i(105),d(100),
=== print_chars(3, chars2, lens1) ===
f(102),i(105),r(114),s(115),t(116),
s(115),e(101),c(99),o(111),n(110),d(100),
t(116),h(104),r(114),i(105),d(100),
=== print_chars(3, chars3, lens2) ===
f(102),i(105),r(114),s(115),t(116), (0),
s(115),e(101),c(99),o(111),n(110),d(100), (0), (0),
t(116),h(104),r(114),i(105),d(100), (0), (0), (0),
*/
C/C++ 中 volatile 关键字详解
volatile 还有另一个作用,可以阻止编译器 CPU 动态调度换序好值类似:x = 1; y = 2;
换序变成:y = 2; x = 1;
的问题,在多线程中比较多见,如下,参考:《程序员的自我修养》中的P30 - 1.6 众人拾柴火焰高
#define barrier() __asm__ volatile ("lwsync")
volatile T* pInst = 0;
T* GetInstance()
{
if (!pInst)
{
lock();
if (!pInst)
{
T* temp = new T;
barrier();
pInst = temp;
}
unlcok();
}
return pInst;
}
const 与 macro,还区别的。
#define NUM 1
和 const int NUM = 1;
是有区别的。
前者没有类型检测的,没有内存地址。前者更准确的说是立即说。
而后者是后类型检测的,也只读内存地址。在编译或链接后出来的文件里分配好,专门储存在只读地址区块内存。
returnType functionName(const paramType paramName);
它表示在函数运行过程中对 paramName
是不可修改的。
returnType functionName(paramType paramName) const;
它是对 functionName
函数的隐式 this
参数的修饰:
returnType functionName(paramType paramName);
没有末端 const
时,functionName
的 this
参数是:const ClassType * this;
returnType functionName(paramType paramName); cosnt
有在末端 const
时,functionName
的 this
参数是:const ClassType * const this;
表示 this 对象内的所有数据不改修改。但如果ClassType 中某个数据成员有 mutable
修饰的话,也是可以修改的,并且不报错,如果没有 mutable
修饰的话,编译会报错。参考:C/C++基础面试-Const的全面理解(C部分)
int p = 0x1;
const int* a1=&p; //变量a1,int*,const,表名指向的数据不可改
int const *a2 = &p; //同a1
int* const a3=&p; //a3指向的地址不能修改
int const* const a4=&p; //什么都不能修改
C++11的constexpr关键字
检查C++11不支持register 与 auto,那就测试一下。
先查看我们的编译器版本
PS D:\jave\Work Files\CPP\register> g++ --version
g++.exe (MinGW.org GCC Build-20200227-1) 9.2.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
测试代码
// jave.lin
#include
#include
#define CPP11 201103
#define CPP14 201402
#define CPP17 201703
int add(int a, int b) { return a + b; }
int main() {
printf("__GNUC__:%d",__GNUC__);
printf("__cplusplus:%d\n",__cplusplus);
#if __cplusplus > CPP14
int i = 0;
int b = add(1, 2);
printf("Not supporting the register and auto keywords, when version greater than c++14.\n");
printf("normally var i:%d\n", ++i);
printf("int b typeid.name() : %s\n", typeid(b).name());
#else
register int i = 0;
auto b = add(1, 2);
printf("Supporting the register and auto keywords, when version lesser than c++14.\n");
printf("register var i:%d\n", ++i);
printf("auto b typeid.name() : %s\n", typeid(b).name());
#endif
return 0;
}
输出
PS D:\jave\Work Files\CPP\register> g++ .\a.cpp -o out -std=c++11
PS D:\jave\Work Files\CPP\register> .\out.exe __GNUC__:9__cplusplus:201103
Supporting the register and auto keywords, when version lesser than c++14.
register var i:1
auto b typeid.name() : i
PS D:\jave\Work Files\CPP\register> g++ .\a.cpp -o out -std=c++17
PS D:\jave\Work Files\CPP\register> .\out.exe __GNUC__:9__cplusplus:201703
Not supporting the register and auto keywords, when version greater than c++14.
normally var i:1
int b typeid.name() : i
// jave.lin
#include
void opt(const int (* optFunc)(int,int), const int a, const int b, int& result) {
if (nullptr == optFunc) return;
result = optFunc(a, b);
}
const int add(int a, int b) { return a + b; }
const int sub(int a, int b) { return a - b; }
int global_i = 0;
int main() {
printf("--lambda format : [capture](parameters)->return-type{body}--\n");
printf("========== normally test ===========\n");
int a = 2;
int b = 3;
int c = ([](int a, int b)-> int { return a + b; })(a, b);
printf("a:%d, b:%d, a + b = c:%d\n", a, b, c);
opt(add, a, b, c);
printf("after opt(add, a, b, c), a:%d, b:%d, c:%d\n", a, b, c);
opt(sub, a, b, c);
printf("after opt(sub, a, b, c), a:%d, b:%d, c:%d\n", a, b, c);
opt([](int a, int b)->const int{ return a * b; }, a, b, c);
printf("after opt(lambda_multi, a, b, c), a:%d, b:%d, c:%d\n", a, b, c);
opt([](int a, int b)->const int{ return a / b; }, a, b, c);
printf("after opt(lambda_div, a, b, c), a:%d, b:%d, c:%d\n", a, b, c);
printf("========== address test ===========\n");
const int (* cb)(int,int) = nullptr;
printf("add func':%p\n", add);
cb = add;
printf("add callback func':%p\n", cb);
printf("sub func':%p\n", sub);
cb = sub;
printf("sub callback func':%p\n", cb);
cb = [](int a, int b)->const int{ return a * b; };
int address1 = (int)cb;
printf("lambda_multi callback func':%p, int:%d\n", cb, address1);
cb = [](int a, int b)->const int{ return a * b; }; // same as above callback
int address2 = (int)cb;
printf("again, lambda_multi callback func':%p, int:%d\n", cb, address2);
printf("callback address diff:%d\n", address2 - address1);
printf("========== capture test ===========\n");
/*
[] // 沒有定义任何变量。使用未定义变量会引发错误。
[x, &y] // x以传值方式传入(默认),y以引用方式传入。
[&] // 任何被使用到的外部变量都隐式地以引用方式加以引用。
[=] // 任何被使用到的外部变量都隐式地以传值方式加以引用。
[&, x] // x显式地以传值方式加以引用。其余变量以引用方式加以引用。
[=, &z] // z显式地以引用方式加以引用。其余变量以传值方式加以引用
[]:默认不捕获任何变量;
[=]:默认以值捕获所有变量;
[&]:默认以引用捕获所有变量;
[x]:仅以值捕获x,其它变量不捕获;
[&x]:仅以引用捕获x,其它变量不捕获;
[=, &x]:默认以值捕获所有变量,但是x是例外,通过引用捕获;
[&, x]:默认以引用捕获所有变量,但是x是例外,通过值捕获;
[this]:通过引用捕获当前对象(其实是复制指针);
[*this]:通过传值方式捕获当前对象;
*/
int f = 0;
([](int& f)->void{f=100;})(f);
printf("==1.f:%d\n", f);
([](int& f){f=200;})(f); // 省略了上面的‘->void’
printf("==2.f:%d\n", f);
([&](){f=300;})(); // 有capture,无parameters,无->,无return type
printf("==3.f:%d\n", f);
int f1 = 0;
int f2 = 0;
int f3 = 0;
([f1,f2,&f3](){int a = f1;int b = 1;f3=1;})(); // 因为f1,f2都是传值,所以只读,f3是引用,所以可以赋值,如果对只读变量赋值会编译报错
printf("==4.f1:%d,f2:%d,f3:%d\n", f1, f2, f3);
([=,&f3](){int a = f1;int b = 1;f3=2;})(); // 因为出了f3其他的外部变量f1,f2都是传值,所以只读,只有f3是引用可赋值,如果对只读变量赋值会编译报错
printf("==5.f1:%d,f2:%d,f3:%d\n", f1, f2, f3);
([]{global_i+=10;})(); // 无capture,无parameters,无->,无return type
printf("==6.global_i:%d\n", global_i);
int x = 0;
([x]()mutable{x=100;})(); // 如果想在lambda修改一个传值的capture x变量值(对外部的变量地址的值不影响),而不会报错可以用mutable
printf("==7.x:%d\n", x); // 因为是传值,所以x的值是不会改变的,但这样可以书写代码方便,所以可以用mutable
return 0;
}
--lambda format : [capture](parameters)->return-type{body}--
========== normally test ===========
a:2, b:3, a + b = c:5
after opt(add, a, b, c), a:2, b:3, c:5
after opt(sub, a, b, c), a:2, b:3, c:-1
after opt(lambda_multi, a, b, c), a:2, b:3, c:6
after opt(lambda_div, a, b, c), a:2, b:3, c:0
========== address test ===========
add func':00401438
add callback func':00401438
sub func':00401445
sub callback func':00401445
lambda_multi callback func':00401506, int:4199686
again, lambda_multi callback func':0040154C, int:4199756
callback address diff:70
========== capture test ===========
==1.f:100
==2.f:200
==3.f:300
==4.f1:0,f2:0,f3:1
==5.f1:0,f2:0,f3:2
==6.global_i:10
==7.x:0
#include
#include
typedef struct S1 S1;
struct S1 {
int value;
};
class C1 {
public:
int value;
std::string name;
C1(int, std::string);
~C1();
};
C1::C1(int v, std::string name) : value(v),name(name) {
printf("call construct func C1.value:%d, name:%s\n", this->value, this->name.data());
}
C1::~C1() {
printf("call destruct func C1.value:%d, name:%s\n", this->value, this->name.data());
}
int main() {
S1 s;
s.value = 1;
printf("->before s.value:%d\n", s.value);
([](S1 s){s.value = 2;})(s);
printf("<-after s.value:%d\n", s.value);
printf("\n");
printf("->before s1.value:%d\n", s.value);
([](S1& s){s.value = 4;})(s);
printf("<-after s1.value:%d\n", s.value);
printf("\n");
C1 c(2, "First"); // alloct在栈上的内存,并调用构造函数
printf("->before c.value:%d\n", c.value);
([](C1 c){c.value = 3;})(c); // 从输出可以看到,函数的Class传值参数,是不会调用构造函数的,alloct在栈上的内存,然后复制数据而已,所以没调用构造函数
printf("<-after c.value:%d\n", c.value);
printf("\n");
C1* c1 = new C1(2, "Second"); // 相当于C的malloc,等,分配内存,但还会调用C++的构造函数
printf("->before c1.value:%d\n", c1->value);
([](C1* c){c->value = 3;})(c1);
printf("<-after c1.value:%d\n", c1->value);
printf("\n");
printf("->before c2.value:%d\n", c1->value);
([](C1& c){c.value = 4;})(*c1);
printf("<-after c2.value:%d\n", c1->value);
delete c1; // 手动调用c1的析构,并free了内存空间
return 0;
// 函数结束后,会自动清理该函数栈帧上的数据,所以c的析构函数会在这里调用
}
->before s.value:1
<-after s.value:1
->before s1.value:1
<-after s1.value:4
call construct func C1.value:2, name:First
->before c.value:2
call destruct func C1.value:3, name:First
<-after c.value:2
call construct func C1.value:2, name:Second
->before c1.value:2
<-after c1.value:3
->before c2.value:3
<-after c2.value:4
call destruct func C1.value:4, name:Second
call destruct func C1.value:2, name:First
// jave.lin - 测试 CPP 的 struct 内存布局
#include
#include
#define SEPARACTOR std::cout << "-----------------------------------------\n";
#define GET_OFFSET(start, special) (((size_t)(void*)special) - ((size_t)(void*)start))
inline const constexpr size_t GetOffset(void* start, void* special) {
return (size_t)special - (size_t)start;
}
template<typename T>
union C1 {
T x;
T r;
T s;
};
template<typename T>
union C2 {
T y;
T g;
T t;
};
template<typename T>
union C3 {
T z;
T b;
T p;
};
template<typename T>
union C4 {
T w;
T a;
T q;
};
template<typename T>
struct vec4 {
C1<T> c1;
C2<T> c2;
C3<T> c3;
C4<T> c4;
};
typedef float GLfloat;
// #pragma pack(4)
// sizeof(Pack_u_t) == 8
// sizeof(Pack_u_t) 会去成员中最大基础数据类型的值来对齐,最大的是 int 4 bytes,所以以 4 bytes 对齐
// 所以 union 中最大的成员占用数据量 char cs[5], 5 个 bytes 应该超出 4 bytes,然后对齐后,要为 4 的倍数,所以就是 8 bytes
union Pack_u_t {
char cs[5];
int i1;
char c1;
};
struct Pack_t {
public:
alignas(4 * 4) char cs[3];
alignas(4 * 4) Pack_u_t u;
alignas(4 * 4) char c1;
alignas(4 * 4) char c2;
};
alignas(4 * 4) char test1;
alignas(4 * 4) char test2;
// #pragma pack()
// #define CLASS // 测试类、结构体,经过测试,结果一样
#ifdef CLASS
# pragma message("Test [Class] Data Block Layout")
# define TEST_TYPE "Class"
class
#else
# pragma message("Test [Struct] Data Block Layout")
# define TEST_TYPE "Struct"
struct
#endif
CameraInfo {
public:
vec4<GLfloat> pos;
vec4<GLfloat> clear_col;
};
int main() {
SEPARACTOR
std::cout << "Test Type : " << TEST_TYPE << "\n";
SEPARACTOR
std::cout << "sizeof(Pack_t) : " << sizeof(Pack_t) << "\n";
std::cout << "sizeof(Pack_u_t) : " << sizeof(Pack_u_t) << "\n";
Pack_t pt;
std::cout << "sizeof(pt.cs) : " << sizeof(pt.cs) << "\n";
std::cout << "sizeof(pt.c1) : " << sizeof(pt.c1) << "\n";
std::cout << "GetOffset(pt, pt.c1) : " << GetOffset(&pt, &pt.c1) << "\n";
std::cout << "GetOffset(test1, test1) : " << GetOffset(&test1, &test1) << "\n";
std::cout << "GetOffset(test1, test2) : " << GetOffset(&test1, &test2) << "\n";
std::cout << "sizeof(size_t) : " << sizeof(size_t) << "\n";
std::cout << "sizeof(void*) : " << sizeof(void*) << "\n";
CameraInfo camInfo;
camInfo.pos = { 1.0f, 2.0f, 3.0f, 4.0f };
camInfo.clear_col = { 1.0f, 1.0f, 0.0f, 1.0f };
SEPARACTOR
std::cout << "Camera Info : \n";
std::cout << " pos : "
<< camInfo.pos.c1.x << ","
<< camInfo.pos.c2.y << ","
<< camInfo.pos.c3.z << ","
<< camInfo.pos.c4.w << "\n"
;
std::cout << " clear_col : "
<< camInfo.clear_col.c1.r << ","
<< camInfo.clear_col.c2.g << ","
<< camInfo.clear_col.c3.b << ","
<< camInfo.clear_col.c4.a << "\n"
;
SEPARACTOR
std::cout << "Camera Info.pos's offset1 : " << GET_OFFSET(&camInfo, &camInfo.pos) << "\n";
std::cout << "Camera Info.pos's offset2 : " << GetOffset(&camInfo, &camInfo.pos) << "\n";
SEPARACTOR
std::cout << "Camera Info.clear_col's offset1 : " << GET_OFFSET(&camInfo, &camInfo.clear_col) << "\n";
std::cout << "Camera Info.clear_col's offset2 : " << GetOffset(&camInfo, &camInfo.clear_col) << "\n";
SEPARACTOR
std::cout << "Camera Info.pos.c1.x's offset : " << GetOffset(&camInfo, &camInfo.pos.c1.x) << "\n";
std::cout << "Camera Info.pos.c2.y's offset : " << GetOffset(&camInfo, &camInfo.pos.c2.y) << "\n";
std::cout << "Camera Info.pos.c3.z's offset : " << GetOffset(&camInfo, &camInfo.pos.c3.z) << "\n";
std::cout << "Camera Info.pos.c4.w's offset : " << GetOffset(&camInfo, &camInfo.pos.c4.w) << "\n";
SEPARACTOR
std::cout << "Camera Info.clear_col.c1.x's offset : " << GetOffset(&camInfo, &camInfo.clear_col.c1.x) << "\n";
std::cout << "Camera Info.clear_col.c2.y's offset : " << GetOffset(&camInfo, &camInfo.clear_col.c2.y) << "\n";
std::cout << "Camera Info.clear_col.c3.z's offset : " << GetOffset(&camInfo, &camInfo.clear_col.c3.z) << "\n";
std::cout << "Camera Info.clear_col.c4.w's offset : " << GetOffset(&camInfo, &camInfo.clear_col.c4.w) << "\n";
SEPARACTOR
std::cout << "&cameInfo : " << &camInfo << "\n";
std::cout << "(void*)&camInfo : " << (void*)&camInfo << "\n";
SEPARACTOR
std::cout << "sizeof(camInfo) : " << sizeof(camInfo) << "\n";
std::cout << "sizeof(CameraInfo) : " << sizeof(CameraInfo) << "\n";
SEPARACTOR
GLfloat* ptr = (GLfloat*)&camInfo;
const size_t len = sizeof(camInfo);
std::cout << "*(ptr) : ";
for (size_t i = 0; i < 8; i++) {
std::cout << "[" << i << "]:" << *(ptr + i) << ",";
}
std::cout << "\n";
SEPARACTOR
std::cout << "After modify.\n";
vec4<GLfloat> col = { 0.5f, 0.5f, 0.0f, 1.0f };
size_t ptr_address = (size_t)ptr;
memcpy(
(void*)(ptr_address + GetOffset(&camInfo, &camInfo.clear_col)),
&col,
sizeof(camInfo.clear_col));
// for (size_t i = 0; i < 8; i++) {
// std::cout << "*(ptr + " << i << ") : " << *(ptr + i) << "\n";
// }
std::cout << "*(ptr) : ";
for (size_t i = 0; i < 8; i++) {
std::cout << "[" << i << "]:" << *(ptr + i) << ",";
}
std::cout << "\n";
SEPARACTOR
return 0;
}
/*
-----------------------------------------
Test Type : Struct
-----------------------------------------
sizeof(Pack_t) : 64
sizeof(Pack_u_t) : 8
sizeof(pt.cs) : 3
sizeof(pt.c1) : 1
GetOffset(pt, pt.c1) : 32
GetOffset(test1, test1) : 0
GetOffset(test1, test2) : 16
sizeof(size_t) : 4
sizeof(void*) : 4
-----------------------------------------
Camera Info :
pos : 1,2,3,4
clear_col : 1,1,0,1
-----------------------------------------
Camera Info.pos's offset1 : 0
Camera Info.pos's offset2 : 0
-----------------------------------------
Camera Info.clear_col's offset1 : 16
Camera Info.clear_col's offset2 : 16
-----------------------------------------
Camera Info.pos.c1.x's offset : 0
Camera Info.pos.c2.y's offset : 4
Camera Info.pos.c3.z's offset : 8
Camera Info.pos.c4.w's offset : 12
-----------------------------------------
Camera Info.clear_col.c1.x's offset : 16
Camera Info.clear_col.c2.y's offset : 20
Camera Info.clear_col.c3.z's offset : 24
Camera Info.clear_col.c4.w's offset : 28
-----------------------------------------
&cameInfo : 0x61fe90
(void*)&camInfo : 0x61fe90
-----------------------------------------
sizeof(camInfo) : 32
sizeof(CameraInfo) : 32
-----------------------------------------
*(ptr) : [0]:1,[1]:2,[2]:3,[3]:4,[4]:1,[5]:1,[6]:0,[7]:1,
-----------------------------------------
After modify.
*(ptr) : [0]:1,[1]:2,[2]:3,[3]:4,[4]:0.5,[5]:0.5,[6]:0,[7]:1,
-----------------------------------------
*/
#include
#include
#include
class Human {
public:
std::string name;
unsigned char age;
Human(std::string, unsigned char);
~Human();
};
Human::Human(std::string name, unsigned char age) : name(name), age(age) {
printf("construct %s, name:%s, age:%d\n", typeid(this).name(), name.data(), age);
}
Human::~Human() {
printf("destruct %s, name:%s, age:%d\n", typeid(this).name(), name.data(), age);
}
class Tony : public Human {
public:
Tony();
};
Tony::Tony() : Human("Tony", 18) {
printf("construct %s, name:%s, age:%d\n", typeid(this).name(), name.data(), age);
}
class Jerry {
};
int main() {
Tony t;
printf("===1===typeid(t).name():%s\n", typeid(t).name());
Human h = (Human)t;
printf("===2===typeid(h).name():%s\n", typeid(h).name());
Tony* tt = new Tony();
Human* hh = dynamic_cast<Human*>(tt);
printf("===3===typeid(hh).name():%s\n", typeid(hh).name());
Jerry* j = new Jerry();
printf("===4===typeid(j).name():%s\n", typeid(j).name());
void* vj = j;
printf("===5===typeid(vj).name():%s\n", typeid(vj).name());
// Human* hhh = dynamic_cast(j); // 编译不过,因为原始类型与目标类型没有上下继承关系
Human* hhh = (Human*)(j);
printf("===6===typeid(hhh).name():%s, hhh'':%p\n", typeid(hhh).name(), hhh);
return 0;
}
/*
construct P5Human, name:Tony, age:18
construct P4Tony, name:Tony, age:18
===1===typeid(t).name():4Tony
===2===typeid(h).name():5Human
construct P5Human, name:Tony, age:18
construct P4Tony, name:Tony, age:18
===3===typeid(hh).name():P5Human
===4===typeid(j).name():P5Jerry
===5===typeid(vj).name():Pv // 从这个输出可以看出,啥都可以转为void *类型,而且C++的typeid(obj).name()值只会返回当前声明类型的名称
===6===typeid(hhh).name():P5Human, hhh'':00EF6E60
destruct P5Human, name:Tony, age:18
destruct P5Human, name:Tony, age:18
*/
可查看:C++ 重载运算符和重载函数
分类 | 符号 |
---|---|
双目算术运算符 | + (加),-(减),*(乘),/(除),% (取模) |
关系运算符 | ==(等于),!= (不等于),< (小于),> (大于>,<=(小于等于),>=(大于等于) |
逻辑运算符 | ||(逻辑或),&&(逻辑与),!(逻辑非) |
单目运算符 | + (正),-(负),*(指针),&(取地址) |
自增自减运算符 | ++(自增),–(自减) |
位运算符 | | (按位或),& (按位与),~(按位取反),^(按位异或),,<< (左移),>>(右移) |
赋值运算符 | =, +=, -=, *=, /= , % = , &=, |
空间申请与释放 | new, delete, new[ ] , delete[] |
其他运算符 | ()(函数调用),->(成员访问),,(逗号), |
符号 | 分类 |
---|---|
.: | 成员访问运算符 |
., ->: | 成员指针访问运算符 |
::: | 域运算符 |
sizeof: | 长度运算符 |
?:: | 条件运算符 |
#: | 预处理符号 |
#include
#include
#include
class A {
public:
std::string name;
A(std::string);
A(const A&);
~A();
A& operator=(const A&);
A& operator=(const std::string);
bool operator==(const A&) const;
// 等,各种运算符都可以重载
};
A::A(std::string name) : name(name) {
printf("construct typeid.name:%s, name:%s\n", typeid(this).name(), name.data());
}
A::A(const A& copyFrom) : name(copyFrom.name) {
printf("copy construct typeid.name:%s, name:%s\n", typeid(this).name(), name.data());
}
A::~A() {
printf("destruct typeid.name:%s, name:%s\n", typeid(this).name(), name.data());
}
A& A::operator=(const A ©From) {
printf("A& A::operator=(const A ©From), src:%s, dest:%s\n", this->name.data(), copyFrom.name.data());
if (this == ©From) {
printf("avoid copy self\n");
return *this; // 避免自己对自己赋值
}
this->name = copyFrom.name;
return *this;
}
A& A::operator=(const std::string n) {
printf("A& A::operator=(const std::string name), src:%s, dest:%s\n", this->name.data(), n.data());
this->name = n;
return *this;
}
bool A::operator==(const A &dest) const {
printf("bool A::operator==(const A &dest), cmp(src:%s, dest:%s)==%d\n", this->name.data(), dest.name.data(), this->name == dest.name);
return this->name == dest.name;
}
void callCopyConstruct(A a) {
// noops
}
int main() {
// on stack obj
A a("Tom");
A a1("Jerry");
printf("==1==cmp, a == a1 : %d\n", a == a1);
// printf方法参数调用了A,则会调用copy construct
printf("==2==a.name:%s, a1.name:%s, a':%p, a1':%p\n", a.name.data(), a1.name.data(), a, a1);
printf("-->before copy from a1\n");
a = a1;
printf("<--after copy from a1\n");
// printf方法参数调用了A,则会调用copy construct
printf("==3==a.name:%s, a1.name:%s, a':%p, a1':%p\n", a.name.data(), a1.name.data(), a, a1);
printf("==4==mp, a == a1 : %d\n", a == a1);
a.name = "Testing";
printf("-->before callCopyConstruct\n");
callCopyConstruct(a);
printf("<--after callCopyConstruct\n");
printf("-->before a1.name = a.name\n");
a1.name = a.name;
printf("<--after a1.name = a.name\n");
printf("==5==cmp, a == a1 : %d\n", a == a1);
printf("-->before operator=(const std::string)\n");
a = "Davide";
printf("<--after operator=(const std::string)\n");
printf("-->before copy self\n");
a = a;
printf("<--after copy self\n");
printf("==6==cmp, a == a1 : %d\n", a == a1);
return 0;
}
construct typeid.name:P1A, name:Tom
construct typeid.name:P1A, name:Jerry
bool A::operator==(const A &dest), cmp(src:Tom, dest:Jerry)==0
==1==cmp, a == a1 : 0
copy construct typeid.name:P1A, name:Jerry
copy construct typeid.name:P1A, name:Tom
==2==a.name:Tom, a1.name:Jerry, a':0061FE94, a1':0061FE7C
destruct typeid.name:P1A, name:Tom
destruct typeid.name:P1A, name:Jerry
-->before copy from a1
A& A::operator=(const A ©From), src:Tom, dest:Jerry
<--after copy from a1
copy construct typeid.name:P1A, name:Jerry
copy construct typeid.name:P1A, name:Jerry
==3==a.name:Jerry, a1.name:Jerry, a':0061FEC4, a1':0061FEAC
destruct typeid.name:P1A, name:Jerry
destruct typeid.name:P1A, name:Jerry
bool A::operator==(const A &dest), cmp(src:Jerry, dest:Jerry)==1
==4==mp, a == a1 : 1
-->before callCopyConstruct
copy construct typeid.name:P1A, name:Testing
destruct typeid.name:P1A, name:Testing
<--after callCopyConstruct
-->before a1.name = a.name
<--after a1.name = a.name
bool A::operator==(const A &dest), cmp(src:Testing, dest:Testing)==1
==5==cmp, a == a1 : 1
-->before operator=(const std::string)
A& A::operator=(const std::string name), src:Testing, dest:Davide
<--after operator=(const std::string)
-->before copy self
A& A::operator=(const A ©From), src:Davide, dest:Davide
avoid copy self
<--after copy self
bool A::operator==(const A &dest), cmp(src:Davide, dest:Testing)==0
==6==cmp, a == a1 : 0
destruct typeid.name:P1A, name:Testing
destruct typeid.name:P1A, name:Davide
// jave.lin
#include
class Puppy {
public:
void setValue(int);
private:
friend void friendPrint(Puppy*);
friend class Master;
int value; // 私有变量
void privateMethod();
};
void Puppy::setValue(int value) {
this->value = value; // 友元可以访问所有成员,一样可以访问私有的
}
void Puppy::privateMethod() {
printf("Puppy::privateMethod\n");
}
class Master {
public:
void print(Puppy*);
void callPuppyPrivateMethod(Puppy*);
};
void Master::print(Puppy* puppy) {
printf("friend Master print(), puppy.value:%d\n", puppy->value);
}
void Master::callPuppyPrivateMethod(Puppy* puppy) {
printf("Master call : ");
puppy->privateMethod();
}
void friendPrint(Puppy* puppy) { // 友元函数不属于任意对象的,有点像static静态函数,或是全局函数似的
printf("friend print(), puppy.value:%d\n", puppy->value);
}
int main() {
Puppy puppy;
puppy.setValue(999);
Master master;
friendPrint(&puppy);
master.print(&puppy);
master.callPuppyPrivateMethod(&puppy);
return 0;
}
/*
输出:
friend print(), puppy.value:999
friend Master print(), puppy.value:999
Master call : Puppy::privateMethod
*/
// jave.lin
#include
class A {
public:
// error: ISO C++ forbids in-class initialization of non-const static member 'A::value'
// static int value = -1; // 这里会报错,非const的static不能在类中定义并初始化
static int value;
const static int const_value = 2000; // const static则可以
static void addOne();
int aaa = -1; // 非静态static的变量可以初始化
private:
static void privateMethod();
friend class B;
};
class B{
public:
static void CallAPrivateMethod();
};
int A::value = 999;
void A::addOne() {
A::value++;
}
void A::privateMethod() {
printf("this is Class A privateMethod.\n");
}
void B::CallAPrivateMethod() {
printf("enter Class B CallAPrivateMethod.\n");
A::privateMethod();
printf("exit Class B CallAPrivateMethod.\n");
}
// 在多文件共享全局的变量无论是否const、static都可以直接初始化
int s = 18;
static int s1 = 19;
const static int s2 = 20;
int main() {
A a;
printf("A'aaa:%d\n", a.aaa);
printf("A::value:%d\n", A::value);
A::addOne();
printf("A::value:%d\n", A::value);
B::CallAPrivateMethod();
printf("A::const_value:%d\n", A::const_value);
printf("global s : %d\n", s);
printf("static global s1 : %d\n", s1);
printf("const static global s2 : %d\n", s2);
return 0;
}
A'aaa:-1
A::value:999
A::value:1000
enter Class B CallAPrivateMethod.
this is Class A privateMethod.
exit Class B CallAPrivateMethod.
A::const_value:2000
global s : 18
static global s1 : 19
const static global s2 : 20
// jave.lin
#include
class V {
public:
virtual void v_func1(); // 只有声明,没有函数体虚函数也必须派生类重载
virtual void v_func2()=0; // 纯虚函数如果派生类不重载会编译报错
virtual void v_func3() { // 有实现的纯虚函数,在派生类可以不重载
printf("class V's v_func3.\n");
}
void func5() {
printf("class V's func5.\n");
}
};
class V1 : public V {
public:
virtual void v_func1();
virtual void v_func3() { // 在内部声明、定义重载
printf("class V1's override virtual v_func3.\n");
}
void v_func2(); // 在内部声明重载
virtual void v_func4(const char* title) {
printf("%s, class V1's v_func4.\n", title);
}
void func5() {
printf("class V1's func5.\n");
}
};
void V1::v_func2() { // 在外部定义重载
printf("class A's override virtual v_func2.\n");
}
class A : public V1 {
public:
void v_func1() {
printf("class A's override virtual v_func1.\n");
}
virtual void v_func4(const char* title) {
printf("%s, class A's v_func4.\n", title);
}
void func5() {
printf("class A's func5.\n");
}
};
int main() {
A a;
a.v_func1();
a.v_func2();
a.v_func3(); // class A没有重载,所以调用的是它的上一级基类V1的函数,而不是V类的函数
a.v_func4("a call:");
a.func5(); // 注意不是virtual虚函数,调用的是对应的类定义下的函数
V1* v1 = dynamic_cast<V1*>(&a);
v1->v_func4("v1 call:"); // 即使转为V1,到时编译时,决定了A重写了v_func4,所以vtable中的v_func4使用的是A的
v1->func5(); // 注意不是virtual虚函数,调用的是对应的类定义下的函数
V* v= dynamic_cast<V*>(v1);
v->func5(); // 注意不是virtual虚函数,调用的是对应的类定义下的函数
return 0;
}
/*
输出:
class A's override virtual v_func1.
class A's override virtual v_func2.
class V1's override virtual v_func3.
a call:, class A's v_func4.
class A's func5.
v1 call:, class A's v_func4.
class V1's func5.
class V's func5.
*/
虚函数,在每个类蓝图都有一个virtual function table,虚拟函数表
每个表的相同函数签名的函数只有一个。即:在编译时如果派生类覆盖了,那么就不会使用基类的,即使转换类型为其他基类型也是使用覆盖的函数,因为就只有一个。
而普通的函数则不同,所有函数都在对应的类蓝图定义下的函数表中,根据函数签名一一对应的。
即:只要将类型转换到当前对转换到对应的类,就会调用对用类定义下的函数。
// jave.lin
#include
#include
#include
#define P1 printf("%s %s().\n", typeid(this).name(), __func__)
#define P2 printf("%s %s(), same_field:%s.\n", typeid(this).name(), __func__, same_field.c_str())
class A{
public:
int a = 1;
std::string same_field = "A_str";
void a_method() { P1; }
void same_method() { P2; }
virtual void same_v_method() { P2; }
};
class B {
public:
int b = 2;
std::string same_field = "B_str";
void b_method() { P1; }
void same_method() { P2; }
virtual void same_v_method() { P2; }
};
class C : public A, public B {
public:
std::string same_field = "C_str";
void same_method() { P2; }
virtual void same_v_method() { P2; }
};
int main() {
C c;
printf("c.a:%d, c.b:%d\n", c.a, c.b);
c.a_method();
c.b_method();
c.A::same_method(); // 相同签名函数,一定要指定所在类
c.B::same_method();
c.C::same_method();
printf("c.same_field:%s\n", c.same_field.c_str()); // 字段名继承关系上有相同,可以不指定,默认使用当前类
printf("((A)c).same_field:%s\n", ((A)c).same_field.c_str()); // 字段名继承关系上有相同,可以不指定,默认使用当前类
printf("((B)c).same_field:%s\n", ((B)c).same_field.c_str()); // 字段名继承关系上有相同,可以不指定,默认使用当前类
c.A::same_v_method(); // 相同签名函数,一定要指定所在类
c.B::same_v_method();
c.C::same_v_method();
printf("\n====== A* c1 = (A*)(new C()); ======\n");
A* a = (A*)(new C());
// 调用虚拟函数表中覆盖的签名函数,这个是根据继承关系中子类定义的覆盖签名函数来确定的
// 明显最后虚拟函数表覆盖的是C类的
a->same_v_method();
return 0;
}
/*
输出:
c.a:1, c.b:2
P1A a_method().
P1B b_method().
P1A same_method(), same_field:A_str.
P1B same_method(), same_field:B_str.
P1C same_method(), same_field:C_str.
c.same_field:C_str
((A)c).same_field:A_str
((B)c).same_field:B_str
P1A same_v_method(), same_field:A_str.
P1B same_v_method(), same_field:B_str.
P1C same_v_method(), same_field:C_str.
====== A* c1 = (A*)(new C()); ======
P1C same_v_method(), same_field:C_str.
*/
#include
class A {
public:
A() {
std::cout << "A ctor\n";
}
virtual ~A() {
std::cout << "A ~desctor\n";
}
};
class B {
public:
B() {
std::cout << "B ctor\n";
}
virtual ~B() {
std::cout << "B ~desctor\n";
}
};
class Test1 : public A, public B {
public:
Test1() {
std::cout << "Test1 ctor\n";
}
virtual ~Test1() {
std::cout << "Test1 ~desctor\n";
}
};
class Test2 : public B, public A {
public:
Test2() {
std::cout << "Test2 ctor\n";
}
virtual ~Test2() {
std::cout << "Test2 ~desctor\n";
}
};
int main() {
std::cout << " === start test1 ===\n";
Test1 t1;
t1.~Test1();
std::cout << " === start test2 ===\n";
Test2 t2;
t2.~Test2();
std::cout << " === end ===\n";
return 0;
}
/* 输出:
=== start test1 ===
A ctor
B ctor
Test1 ctor
Test1 ~desctor
B ~desctor
A ~desctor
=== start test2 ===
B ctor
A ctor
Test2 ctor
Test2 ~desctor
A ~desctor
B ~desctor
=== end ===
Test2 ~desctor
A ~desctor
B ~desctor
Test1 ~desctor
B ~desctor
A ~desctor
*/
class MouseInfo {
public:
static double x, y;
static double deltaX, deltaY;
};
// 如果没有下面的初始化 static 就会编译包 LNK 2001 错误
double MouseInfo::x = 0;
double MouseInfo::y = 0;
double MouseInfo::deltaX = 0;
double MouseInfo::deltaY = 0;
C++ 内存分配(new,operator new)详解
c++中的new、operator new、placement new
C++ pair的基本用法总结(整理)
#if COMMENT
std::pair<class FirstType, class SecondType>
#endif
// init
std::pair<std::string, int> person("tony", 18);
// get
printf("name:%s, age:%d\n", person.first.c_str(), person.second);
详细参考:std::get(std::pair)
#include
#include
int main()
{
auto p = std::make_pair(1, 3.14);
std::cout << '(' << std::get<0>(p) << ", " << std::get<1>(p) << ")\n";
std::cout << '(' << std::get<int>(p) << ", " << std::get<double>(p) << ")\n";
}
/* 输出
(1, 3.14)
(1, 3.14)
*/
在complex头文件定义可以看到:
...
template <class _Ty>
struct _Complex_value {
enum { _Re = 0, _Im = 1 };
_Ty _Val[2];
};
// CLASS TEMPLATE _Complex_base
template <class _Ty, class _Valbase>
class _Complex_base : public _Valbase {
public:
using _Myctraits = _Ctraits<_Ty>;
using value_type = _Ty;
constexpr _Complex_base(const _Ty& _Realval, const _Ty& _Imagval) : _Valbase{{_Realval, _Imagval}} {}
_Ty real(const _Ty& _Right) { // set real component
return this->_Val[_RE] = _Right;
}
_Ty imag(const _Ty& _Right) { // set imaginary component
return this->_Val[_IM] = _Right;
}
_NODISCARD constexpr _Ty real() const { // return real component
return this->_Val[_RE];
}
_NODISCARD constexpr _Ty imag() const { // return imaginary component
return this->_Val[_IM];
}
...
可以看到实质就是一个:struct结构体,里面有个_Ty _Val[2]
数组而已
template <class _Ty>
struct _Complex_value {
enum { _Re = 0, _Im = 1 };
_Ty _Val[2];
};
每次我们real()
就是return _Val[_Re]
,real(float v)
就是_Val[_Re] = v;
// jave.lin
#include
#include
int main() {
std::complex<float> f1(-1.0, 1.0 );
std::complex<float> f2( 1.0, -1.0 );
std::complex<float> f3( 3.0 ); // 一个构造参数,为real实数值,虚数默认为0
std::cout << "f1 = " << f1 << "\n";
std::cout << "f2 = " << f2 << "\n";
std::cout << "f3 = " << f3 << "\n";
std::cout << "f1 + f2 = " << f1 + f2 << "\n";
std::cout << "f1 + f3 = " << f1 + f3 << "\n";
f1.real(3.0);
f1.imag(4.0);
std::cout << "f1 = " << f1 << "\n";
float a = f1.real() + f1.imag();
std::cout << "a = " << a << "\n";
return 0;
}
/*输出
f1 = (-1,1)
f2 = (1,-1)
f3 = (3,0)
f1 + f2 = (0,0)
f1 + f3 = (2,1)
f1 = (3,4)
a = 7
*/
C++ set用法总结(整理)
#include
#include
#include
#define P(set, type)\
{\
std::cout << #set << "'s size : " << set.size() << "\n";\
type::iterator begin = set.begin();\
for (; begin != set.end(); begin++) {\
std::cout << *begin << ", ";\
}\
std::cout << "\n";\
}
#define EXSIT(set, type, key)\
{\
type::iterator ret = set.find(key);\
if (ret != set.end()) {\
std::cout << "Found the : " << *ret << "\n";\
}\
}
void exsit(std::set<const char*> set, const char* key) {
std::set<const char*>::iterator ret = set.find(key);
if (ret != set.end()) {
std::cout << "In [exsit funcs] " << "\"" << key << "\" was Found\n";
} else {
std::cout << "In [exsit funcs] " << "\"" << key << "\" is not Found\n";
}
}
int main() {
// int set
std::set<int> set_int;
// 插入
set_int.insert(1);
set_int.insert(1); // 不会有重复的插入
set_int.insert(2);
set_int.insert(3);
P(set_int, std::set<int>)
// 删除
set_int.erase(1);
set_int.erase(2);
set_int.erase(4); // 删除一个不存在的,也不会报错
P(set_int, std::set<int>)
// 初始化插入
int arr[] = {9,8,7};
std::set<int> set1(arr, arr + 3);
P(set1, std::set<int>)
// 遍历打印、查找
std::set<const char*> set_chars;
set_chars.insert("one");
set_chars.insert("two");
set_chars.insert("three");
P(set_chars, std::set<const char*>) // std::set 是有序的,所以可以按插入顺序
EXSIT(set_chars, std::set<const char*>, "two")
exsit(set_chars, "two");
std::string key_name = "this is key";
set_chars.insert(key_name.c_str());
P(set_chars, std::set<const char*>)
EXSIT(set_chars, std::set<const char*>, key_name.c_str())
exsit(set_chars, key_name.c_str());
// 清楚
set_chars.clear();
P(set_chars, std::set<const char*>)
return 0;
}
/* 输出:
set_int's size : 3
1, 2, 3,
set_int's size : 1
3,
set1's size : 3
7, 8, 9,
set_chars's size : 3
one, two, three,
Found the : two
In [exsit funcs] "two" was Found
set_chars's size : 4
one, two, three, this is key,
Found the : this is key
In [exsit funcs] "this is key" was Found
set_chars's size : 0
*/
C++ map用法总结(整理)
#include
#include
#include
#include
#include
using _t = std::map<const char*, int>;
using _uo_t = std::unordered_map<const char*, int>;
using _uo_hash_t = std::unordered_map<int, int>;
void print_chars(const char* title, const char* chars) {
std::cout << title << ": ";
while(true) {
std::cout << *chars << "(" << (unsigned int)(*chars) << ")" << ",";
if (*(++chars) == 0) {
std::cout << "\\0";
break;
}
}
std::cout << '\n';
}
void printf_map_find(_t* m, std::string key_name) {
print_chars("In [printf_map_find funs]", key_name.c_str());
_t::const_iterator ret = m->find(key_name.c_str());
if (ret != m->end()) {
std::cout << "Found the key : " << (*ret).first << ", value : " << (*ret).second << "\n";
} else {
std::cout << "Not found the key : " << "this is key" << "\n";
}
}
void printf_unordered_map_find(_uo_t* m, std::string key_name) {
print_chars("In [printf_unordered_map_find funs]", key_name.c_str());
_uo_t::const_iterator ret = m->find(key_name.c_str());
if (ret != m->end()) {
std::cout << "Found the key : " << (*ret).first << ", value : " << (*ret).second << "\n";
} else {
std::cout << "Not found the key : " << "this is key" << "\n";
}
}
int getHash(const char* chars) {
int hash = 0;
while((*chars) != 0) {
hash = hash * 31 + (*chars++);
}
return hash;
}
int getHash(std::string str) {
return getHash(str.c_str());
}
void printf_unordered_map_find_by_hash(_uo_hash_t* m, std::string key_name) {
std::cout << "In [printf_unordered_map_find_by_hash funcs]\n";
int hash_code = getHash(key_name);
std::cout << key_name << " ==> hash code : " << hash_code << "\n";
_uo_hash_t::const_iterator ret = m->find(hash_code);
if (ret != m->end()) {
std::cout << "Found the key : " << (*ret).first << ", value : " << (*ret).second << "\n";
} else {
std::cout << "Not found the key : " << "this is key" << "\n";
}
}
int main() {
_t* m = new _t();
//
// testing insert
//
std::cout << " === testing std::map insert ===\n";
m->insert(std::pair<const char*, int>("test1", 1));
m->insert(std::pair<const char*, int>("test2", 2));
m->insert(_t::value_type("test3", 3));
// 注意 map与vector的iterator 没有重载+运算符,所以用 it + i 的方式不行
// vector是有重载+云算法的
// 但 map 的 iterator 是有实现 ++ 运算符的,所以可以使用下列方式来遍历
_t::iterator it = m->begin();
for (; it != m->end(); it++) {
std::cout << it->first << ":" << it->second << "\n";
}
//
// testing find
//
//
// 测试 std::map ,让 const char* 作为 key,发现跨函数调用会有问题
//
std::cout << " === testing std::map find ===\n";
std::cout << " ## using std::string.c_str() for std::map's key ##\n";
std::string key_name = "this is key";
m->insert(_t::value_type(key_name.c_str(), 99));
// 在main 函数内打印 key_name.c_str() 与 在 printf_map_find 函数内的 key_name.c_str() 打印内容是一样的
print_chars("In [main funcs]", key_name.c_str());
// _t::const_iterator ret = m->find("this is key"); // 这里注意,明文字符串也查找不了
_t::const_iterator ret = m->find(key_name.c_str()); // 但没有跨函数来传入 key_name.c_str() 的查找是能找到的
if (ret != m->end()) {
std::cout << "Found the key : " << (*ret).first << ", value : " << (*ret).second << "\n";
} else {
std::cout << "Not found the key : " << "this is key" << "\n";
}
std::cout << "---------------------\n";
printf_map_find(m, key_name);
std::cout << "\n\n";
//
// 使用 std::unordered_map 无序的 hash_map 会有问题,可能自己对底层 unordered_map 不太了解
//
std::cout << " === testing std:::unordered_map find ===\n";
std::cout << " ## using std::string.c_str() for std::unordered_map's key ##\n";
_uo_t* m_uo = new _uo_t();
m_uo->insert(_uo_t::value_type(key_name.c_str(), 99));
print_chars("In [main funcs]", key_name.c_str());
_uo_t::const_iterator ret_uo = m_uo->find(key_name.c_str());
if (ret_uo != m_uo->end()) {
std::cout << "Found the key : " << (*ret_uo).first << ", value : " << (*ret_uo).second << "\n";
} else {
std::cout << "Not found the key : " << "this is key" << "\n";
}
std::cout << "---------------------\n";
printf_unordered_map_find(m_uo, key_name);
std::cout << "\n\n";
//
// 使用 hash code 就没有上面的字符串的问题
//
std::cout << " === testing std:::unordered_map hash key find ===\n";
std::cout << " ## using hash(std::string.c_str()) for std::unordered_map's key ##\n";
_uo_hash_t* m_uo_hash = new _uo_hash_t();
m_uo_hash->insert(_uo_hash_t::value_type(getHash(key_name.c_str()), 99));
_uo_hash_t::const_iterator ret_uo_hash = m_uo_hash->find(getHash(key_name.c_str()));
if (ret_uo_hash != m_uo_hash->end()) {
std::cout << "Found the key : " << (*ret_uo_hash).first << ", value : " << (*ret_uo_hash).second << "\n";
} else {
std::cout << "Not found the key : " << "this is key" << "\n";
}
std::cout << "---------------------\n";
printf_unordered_map_find_by_hash(m_uo_hash, key_name);
return 0;
}
/* 输出:
=== testing std::map insert ===
test1:1
test2:2
test3:3
=== testing std::map find ===
## using std::string.c_str() for std::map's key ##
In [main funcs]: t(116),h(104),i(105),s(115), (32),i(105),s(115), (32),k(107),e(101),y(121),\0
Found the key : this is key, value : 99
---------------------
In [printf_map_find funs]: t(116),h(104),i(105),s(115), (32),i(105),s(115), (32),k(107),e(101),y(121),\0
Not found the key : this is key
=== testing std:::unordered_map find ===
## using std::string.c_str() for std::unordered_map's key ##
In [main funcs]: t(116),h(104),i(105),s(115), (32),i(105),s(115), (32),k(107),e(101),y(121),\0
Found the key : this is key, value : 99
---------------------
In [printf_unordered_map_find funs]: t(116),h(104),i(105),s(115), (32),i(105),s(115), (32),k(107),e(101),y(121),\0
Not found the key : this is key
=== testing std:::unordered_map hash key find ===
## using hash(std::string.c_str()) for std::unordered_map's key ##
Found the key : -2046255573, value : 99
---------------------
In [printf_unordered_map_find_by_hash funcs]
this is key ==> hash code : -2046255573
Found the key : -2046255573, value : 99
*/
// jave.lin
#include
#include
#include
#include
// 升序
bool cmp_ascending(int a, int b) {
return a < b;
}
// 降序
bool cmp_descending (int a, int b) {
return a > b;
}
// 查找
int find_vec(std::vector<int> vec, int find_val, bool print = true) {
std::vector<int>::iterator ret_it = find(vec.begin(), vec.end(), find_val);
if (ret_it != vec.end()) {
if (print) {
std::cout << "find(" << find_val << ") : " << *ret_it;
std::cout << ", idx : " << ret_it - vec.begin() << "\n";
}
return ret_it - vec.begin();
} else {
if (print) {
std::cout << "find(" << find_val << ") : not found\n";
}
return -1;
}
}
// 打印
void print(const char* title, std::vector<int> vec) {
std::cout << title << ":\n";
for (size_t i = 0; i < vec.size(); i++) {
std::cout << vec.at(i) << ",";
}
std::cout << "\n";
}
int main() {
// init
int arr[10] = { 3, 0, 65, 7, 32, 1, 4, 76, 8, 4};
std::vector<int> test_vec(arr, arr + 10);
print("init", test_vec);
// sort
sort(test_vec.begin(), test_vec.end());
print("after sort", test_vec);
// sort with compare function - ascending order
sort(test_vec.begin(), test_vec.end(), cmp_ascending);
print("after sort(a,b,cmp_ascending)", test_vec);
// sort with compare function - descending order
sort(test_vec.begin(), test_vec.end(), cmp_descending);
print("after sort(a,b,cmp_descending)", test_vec);
// push
test_vec.push_back(100); test_vec.push_back(50);
print("after test_vec.push_back(100, 50)", test_vec);
// find
find_vec(test_vec, 50);
find_vec(test_vec, 100);
find_vec(test_vec, 999);
// modify
int idx = find_vec(test_vec, 100, false);
if (idx != -1) {
test_vec.at(idx) = -999;
print("after find 1000 & modify to -999", test_vec);
}
idx = find_vec(test_vec, 50, false);
if (idx != -1) {
test_vec.at(idx) = 1000;
print("after find 50 & modify to 1000", test_vec);
}
// delete
idx = find_vec(test_vec, 4, false);
if (idx != -1) {
test_vec.erase(test_vec.begin() + idx);
print("after find 4 & remove it", test_vec);
}
idx = find_vec(test_vec, 0, false);
if (idx != -1) {
test_vec.erase(test_vec.begin() + idx, test_vec.begin() + idx + 3);
print("after find 0 & remove subsequence 3 element", test_vec);
}
std::vector<int> vec;
vec.push_back(1);
vec.push_back(2);
vec.push_back(3);
std::vector<int>::const_iterator begin = vec.cbegin();
size_t size = vec.size();
for (size_t i = 0; i < size; i++) {
std::cout << "begin pos1 : " << &(*(begin + i)) << "\n";
if (*(begin + i) == 2) {
// erase 会增加 传入的 iterator 的地址,所以使用这种方式遍历来删除没那么容易出错
vec.erase(begin + i);
size--;
i--;
continue;
}
std::cout << "begin pos2 : " << &(*(begin + i)) << "\n";
}
return 0;
}
/* 输出:
init:
3,0,65,7,32,1,4,76,8,4,
after sort:
0,1,3,4,4,7,8,32,65,76,
after sort(a,b,cmp_ascending):
0,1,3,4,4,7,8,32,65,76,
after sort(a,b,cmp_descending):
76,65,32,8,7,4,4,3,1,0,
after test_vec.push_back(100, 50):
76,65,32,8,7,4,4,3,1,0,100,50,
find(50) : 50, idx : 11
find(100) : 100, idx : 10
find(999) : not found
after find 1000 & modify to -999:
76,65,32,8,7,4,4,3,1,0,-999,50,
after find 50 & modify to 1000:
76,65,32,8,7,4,4,3,1,0,-999,1000,
after find 4 & remove it:
76,65,32,8,7,4,3,1,0,-999,1000,
after find 0 & remove subsequence 3 element:
76,65,32,8,7,4,3,1,
begin pos1 : 007814C8
begin pos2 : 007814C8
begin pos1 : 007814CC
begin pos1 : 007814CC
begin pos2 : 007814CC
*/
C++ #define,typedef,using用法区别
define没啥可说,就是预编译的关键字,定义需要宏文字替换的功能。
而typedef, using功能一样,但using是C++11后出来的东西,C++委员会推荐using替换typedef。
因为using可读性比typedef高
随着C++11后,因为委员会的推荐使用using,那么就开始弄个using能做,typedef不能的。
using的编译、运行都正常
template <typename T>
using Vec = MyVector<T, MyAlloc<T>>;
// usage
Vec<int> vec;
typedef的编译会报错error: a typedef cannot be a template
template <typename T>
typedef MyVector<T, MyAlloc<T>> Vec;
// usage
Vec<int> vec;
#include
#define __CONNECT__FLAG(f1, f2) (__##f1##_##f2##__)
#define __CALL(f1, f2) __CONNECT__FLAG(f1, f2)
void __a_b__() {
std::cout << "__a_b__\n";
}
void __c_d__() {
std::cout << "__c_d__\n";
}
int main() {
__CALL(a,b)();
__CALL(c,d)();
return 0;
}
/* 输出:
__a_b__
__c_d__
*/
可以参考:
// jave.lin
// Check GCC
#if __GNUC__
# pragma message("compiling in GNUC, GCC")
# if __x86_64__ || __ppc64__
# pragma message("64 bits computer")
# define ENVIRONMENT64
# else
# pragma message("32 bits computer")
# define ENVIRONMENT32
# endif
#else
// Check windows
# pragma message("compiling Not in GNUC, GCC")
# if _WIN32 || _WIN64
# pragma message("compiling in Window32/64")
# if _WIN64
# pragma message("64 bits computer")
# define ENVIRONMENT64
# else
# pragma message("32 bits computer")
# define ENVIRONMENT32
# endif
# endif
#endif
编译输出:
1、在VS中的MSVC的编译输出:
1>compiling Not in GNUC, GCC
1>compiling in Window32/64
1>32 bits computer
2、在VSC中使用GCC的编译输出:
a.cpp:7:44: note: #pragma message: compiling in GNUC, GCC
7 | # pragma message("compiling in GNUC, GCC")
| ^
a.cpp:12:42: note: #pragma message: 32 bits computer
12 | # pragma message("32 bits computer")
|
#include
#include
#include
bool readFile(const std::string fileName, std::string& result) {
std::fstream ifile;
ifile.open(fileName, std::ios::in);
if (!ifile) return false;
std::string temp;
while(std::getline(ifile, temp)) {
printf("reading line:%s\n", temp.data());
result.append(temp + "\n");
}
ifile.close();
return true;
}
bool writeFile(const std::string fileName, const std::string writeStr) {
std::fstream ofile;
ofile.open(fileName, std::ios::out | std::ios::app);
if (!ofile) {
return false;
}
ofile << writeStr << std::endl;
ofile.close();
return true;
}
bool clearFile(const std::string fileName) {
std::fstream ofile;
ofile.open(fileName, std::ios::out | std::ios::trunc);
if (!ofile) {
return false;
}
ofile.clear();
ofile.close();
return true;
}
int main() {
std::string readStr;
std::string fileName = ".\\MyTxt.txt";
if (readFile(fileName, readStr)) {
printf("%s reading complete:\n%s", fileName.data(), readStr.data());
} else {
printf("%s reading failure\n", fileName.data());
// return -1;
}
printf("input your message:");
std::string writeStr;
std::getline(std::cin, writeStr);
if (writeStr[0] == '#') {
printf("inputing cmd...");
if (writeStr.substr(1) == "clear") {
printf("[clear].\n");
printf("clearing...\n");
if (clearFile(fileName)) {
printf("%s, clear complete!\n", fileName.data());
} else {
printf("%s, clear failure!\n", fileName.data());
}
} else {
printf("un handle cmd : %s\n", writeStr.substr(1).data());
}
} else {
if (writeStr == "") {
printf("empty writing str, cancel writing.\n");
return 0;
}
printf("writeStr:%s\n", writeStr.data());
if (writeFile(fileName, writeStr)) {
printf("%s writing complete:%s\n", fileName.data(), writeStr.data());
} else {
printf("%s writing failure\n", fileName.data());
return -1;
}
}
return 0;
}
PS D:\jave\Work Files\CPP\fstream,ifstream,ofstream> g++ .\a.cpp -o out
PS D:\jave\Work Files\CPP\fstream,ifstream,ofstream> .\out.exe
.\MyTxt.txt reading complete:
input your message:1111111111111111111
writeStr:1111111111111111111
.\MyTxt.txt writing complete:1111111111111111111
PS D:\jave\Work Files\CPP\fstream,ifstream,ofstream> .\out.exe
reading line:1111111111111111111
.\MyTxt.txt reading complete:
1111111111111111111
input your message:22222222222222222
writeStr:22222222222222222
.\MyTxt.txt writing complete:22222222222222222
PS D:\jave\Work Files\CPP\fstream,ifstream,ofstream> .\out.exe
reading line:1111111111111111111
reading line:22222222222222222
.\MyTxt.txt reading complete:
1111111111111111111
22222222222222222
input your message:333333333333333
writeStr:333333333333333
.\MyTxt.txt writing complete:333333333333333
PS D:\jave\Work Files\CPP\fstream,ifstream,ofstream> .\out.exe
reading line:1111111111111111111
reading line:22222222222222222
reading line:333333333333333
.\MyTxt.txt reading complete:
1111111111111111111
22222222222222222
333333333333333
input your message:this is 4th message.
writeStr:this is 4th message.
.\MyTxt.txt writing complete:this is 4th message.
PS D:\jave\Work Files\CPP\fstream,ifstream,ofstream> .\out.exe
reading line:1111111111111111111
reading line:22222222222222222
reading line:333333333333333
reading line:this is 4th message.
.\MyTxt.txt reading complete:
1111111111111111111
22222222222222222
333333333333333
this is 4th message.
input your message:#clear
inputing cmd...[clear].
clearing...
.\MyTxt.txt, clear complete!
PS D:\jave\Work Files\CPP\fstream,ifstream,ofstream> .\out.exe
.\MyTxt.txt reading complete:
input your message:
empty writing str, cancel writing.
C++ seekp和seekg函数用法详解
参考:tutorial2.c
#include
/* A simple function that will read a file into an allocated char pointer buffer */
char* filetobuf(char *file)
{
FILE *fptr;
long length;
char *buf;
fptr = fopen(file, "rb"); /* Open file for reading */
if (!fptr) /* Return NULL on failure */
return NULL;
fseek(fptr, 0, SEEK_END); /* Seek to the end of the file */
length = ftell(fptr); /* Find out how many bytes into the file we are */
buf = (char*)malloc(length+1); /* Allocate a buffer for the entire length of the file and a null terminator */
fseek(fptr, 0, SEEK_SET); /* Go back to the beginning of the file */
fread(buf, length, 1, fptr); /* Read the contents of the file in to the buffer */
fclose(fptr); /* Close the file */
buf[length] = 0; /* Null terminator */
return buf; /* Return the buffer */
}
// jave.lin
#include
#include
typedef struct Exception {
std::string file;
std::string func;
std::string msg;
int line;
} Exception;
#define EXCEPTION(msg) { __FILE__, __func__, #msg, __LINE__ }
void test(void)
// throw(char*, std::string, Exception) // 确定指定异常 C++11 编译会有警告过时,C++17会编译失败
// throw(...) // 不确定异常 C++11 编译会有警告过时,C++17会编译失败
// throw() // 无异常
noexcept // 无异常
{
try {
#ifdef CHAR_E // char* 字符串提示
throw "My 'CHAR_E' Exception.";
#elif STR_E // std::string 字符串提示
std::string eStr("My 'STR_E' Exception.");
throw eStr;
#elif STRUCT_E // 自定义类型
// Exception e = { __FILE__, "struct exception message.", __LINE__ };
Exception e = EXCEPTION(struct exception message.);
throw e;
#else // 内置std::exception 类型
throw std::runtime_error("this is an exception.");
#endif
} catch (const char* &e) {
printf("catch the 'const char* e' exception : %s\n", e);
} catch (std::string &eStr) {
printf("catch the 'std::string eStr' exception : %s\n", eStr.data());
} catch (Exception &e) {
printf("catch the 'Exception e', file:%s, func:%s, message:%s, line:%d\n",
e.file.data(), e.func.data(), e.msg.data(), e.line);
} catch (const std::exception& e) {
std::cout << e.what() << std::endl;
}
}
int main() {
test();
return 0;
}
/*
输出:
PS D:\jave\Work Files\CPP\try...catch> g++ .\a.cpp -o out -DCHAR_E
PS D:\jave\Work Files\CPP\try...catch> .\out.exe
catch the 'const char* e' exception : My 'CHAR_E' Exception.
PS D:\jave\Work Files\CPP\try...catch> g++ .\a.cpp -o out -DSTR_E
PS D:\jave\Work Files\CPP\try...catch> .\out.exe
catch the 'std::string eStr' exception : My 'STR_E' Exception.
PS D:\jave\Work Files\CPP\try...catch> g++ .\a.cpp -o out -DSTRUCT_E
PS D:\jave\Work Files\CPP\try...catch> .\out.exe
catch the 'Exception e', file:.\a.cpp, func:test, message:struct exception message., line:28
PS D:\jave\Work Files\CPP\try...catch> g++ .\a.cpp -o out
PS D:\jave\Work Files\CPP\try...catch> .\out.exe
this is an exception.
PS D:\jave\Work Files\CPP\try...catch>
*/
如果想要更详细的调试信息,可以参考GNU C Library:
可参考:Find Where a C++ Exception is Thrown
throw_exception.cpp
#include
#include
using namespace std;
void function()
{
throw runtime_error("i am an exception");
}
int main()
{
try
{
function();
}
catch(const std::exception& e)
{
cout << e.what() << endl;
}
return 0;
}
~/exception$ make
g++ -g throw_exception.cpp -o throw_exception
~/exception$ gdb throw_exception
...
Reading symbols from throw_exception...done.
(gdb) catch throw
Catchpoint 1 (throw)
(gdb) run
Starting program: throw_exception
Catchpoint 1 (exception thrown), 0x00007ffff7b8f910 in __cxa_throw () from /usr/lib/libstdc++.so.6
(gdb) where
#0 0x00007ffff7b8f910 in __cxa_throw () from /usr/lib/libstdc++.so.6
#1 0x0000000000400d89 in function () at throw_exception.cpp:8
#2 0x0000000000400dca in main () at throw_exception.cpp:15
(gdb)
命名空间会修改函数签名,所以这样就算是两个函数参数,名称一模一样,只要他们命名空间不同也是两个不同的函数,因为最终函数签名不一样了
#include
namespace A {
class clsName {
public:
clsName() {
printf("namespace A class constructor.\n");
}
};
void func() {
printf("namespace A func.\n");
}
const char* str = "namespace A char*.\n";
}
namespace B {
class clsName {
public:
clsName() {
printf("namespace B class constructor.\n");
}
};
void func() {
printf("namespace B func.\n");
}
const char* str = "namespace B char*.\n";
}
int main() {
A::clsName a();
A::func();
printf(A::str);
B::clsName b();
B::func();
printf(B::str);
return 0;
}
/*
输出:
namespace A func.
namespace A char*.
namespace B func.
namespace B char*.
*/
C++泛型与多态(1):基础篇
// jave.lin
#include
#include
#include
// ========== template function ===========
template<typename T>
T* getArray(size_t size) {
printf("getArray, elementType:%s\n", typeid(T).name());
T* result = new T[size];
if (result == NULL) {
printf("out of mem.\n");
}
return result;
}
template<typename T>
void initArray(T arr[], size_t size, T start = (T)0) {
for (size_t i = 0; i < size; i++) {
arr[i] = start + i;
}
printf("initArray, elementType:%s complete!\n", typeid(T).name());
}
template<typename T>
void printArray(const T arr[], size_t size) {
printf("printArray, elementType:%s start...\n", typeid(T).name());
for (size_t i = 0; i < size; i++) {
std::cout << arr[i] << ",";
}
printf("\nprintArray, elementType:%s complete!\n", typeid(T).name());
}
// ========== template class ===========
template<class T>
class Array {
public:
const int getSize() const { return size; }
explicit Array(size_t size) {
printf("template class constructor, elementType:%s\n", typeid(T).name());
this->size = size;
arr = new T[size];
if (arr == NULL) {
printf("out of mem.\n");
}
}
void init(T start = (T)0) {
for (size_t i = 0; i < size; i++) {
*(arr + i) = start + i;
}
printf("template class init(), elementType:%s complete!\n", typeid(T).name());
}
void print() {
printf("template class print(), elementType:%s start...\n", typeid(T).name());
for (size_t i = 0; i < size; i++) {
std::cout << *(arr + i) << ",";
}
printf("\ntemplate class print(), elementType:%s complete!\n", typeid(T).name());
}
private:
T* arr;
size_t size;
};
template<typename T>
void printTypeDefaultValue(T t) {
std::cout << typeid(T).name() << ", t value : " << t << ", default value : " << T() << std::endl;
}
int main() {
printf("====== template function start ======.\n");
printf("----- int start -----.\n");
size_t size = 10;
// 注意最好不使用auto,这里作为反面教材
// 第一:auto可读性会降低
// 第二:auto在C++11会提示过期
// 第三:auto在C++17会编译错误(C++17已不支持了)
auto ia = getArray<int>(size);
initArray<int>(ia, size, -5);
printArray<int>(ia, size);
printf("\n----- double start -----.\n");
auto da = getArray<double>(size);
initArray<double>(da, size, 5.0);
printArray<double>(da, size);
printf("\n----- char start -----.\n");
auto ca = getArray<char>(size);
initArray<char>(ca, size, 'a');
printArray<char>(ca, size);
printf("\n====== template class start ======.\n");
printf("----- short start -----.\n");
Array<short>* g_ia = new Array<short>(10);
g_ia->init(-5);
g_ia->print();
printf("\n----- long double start -----.\n");
Array<long double>* g_lda = new Array<long double>(10);
g_lda->init(10);
g_lda->print();
printf("\n----- float start -----.\n");
Array<float>* g_fa = new Array<float>(10);
g_fa->init(20.0);
g_fa->print();
printf("\n----- pf start -----.\n");
printTypeDefaultValue<char> ('a');
printTypeDefaultValue<std::string> ("this is str");
printTypeDefaultValue<short> (123);
printTypeDefaultValue<int> (123456);
printTypeDefaultValue<long> (123456789);
printTypeDefaultValue<float> (0.1);
printTypeDefaultValue<double> (0.1);
printTypeDefaultValue<long double> (1.1);
return 0;
}
/*
输出:
====== template function start ======.
----- int start -----.
getArray, elementType:i
initArray, elementType:i complete!
printArray, elementType:i start...
-5,-4,-3,-2,-1,0,1,2,3,4,
printArray, elementType:i complete!
----- double start -----.
getArray, elementType:d
initArray, elementType:d complete!
printArray, elementType:d start...
5,6,7,8,9,10,11,12,13,14,
printArray, elementType:d complete!
----- char start -----.
getArray, elementType:c
initArray, elementType:c complete!
printArray, elementType:c start...
a,b,c,d,e,f,g,h,i,j,
printArray, elementType:c complete!
====== template class start ======.
----- short start -----.
template class constructor, elementType:s
template class init(), elementType:s complete!
template class print(), elementType:s start...
-5,-4,-3,-2,-1,0,1,2,3,4,
template class print(), elementType:s complete!
----- long double start -----.
template class constructor, elementType:e
template class init(), elementType:e complete!
template class print(), elementType:e start...
10,11,12,13,14,15,16,17,18,19,
template class print(), elementType:e complete!
----- float start -----.
template class constructor, elementType:f
template class init(), elementType:f complete!
template class print(), elementType:f start...
20,21,22,23,24,25,26,27,28,29,
template class print(), elementType:f complete!
----- pf start -----.
c, t value : a, default value :
NSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE, t value : this is str, default value :
s, t value : 123, default value : 0
i, t value : 123456, default value : 0
l, t value : 123456789, default value : 0
f, t value : 0.1, default value : 0
d, t value : 0.1, default value : 0
e, t value : 1.1, default value : 0
*/
在 C++ Template 中很多地方都用到了 typename 与 class 这两个关键字,而且好像可以替换,是不是这两个关键字完全一样呢?
相信学习 C++ 的人对 class 这个关键字都非常明白,class 用于定义类,在模板引入 c++ 后,最初定义模板的方法为:
template<class T>......
这里 class 关键字表明T是一个类型,后来为了避免 class 在这两个地方的使用可能给人带来混淆,所以引入了 typename 这个关键字,它的作用同 class 一样表明后面的符号为一个类型,这样在定义模板的时候就可以使用下面的方式了:
template<typename T>......
在模板定义语法中关键字 class 与 typename 的作用完全一样。
typename 难道仅仅在模板定义中起作用吗?其实不是这样,typename 另外一个作用为:使用嵌套依赖类型(nested depended name),如下所示:
class MyArray
{
public:
typedef int LengthType;
.....
}
template<class T>
void MyMethod( T myarr )
{
typedef typename T::LengthType LengthType;
LengthType length = myarr.GetLength;
}
这个时候 typename 的作用就是告诉 c++ 编译器,typename 后面的字符串为一个类型名称,而不是成员函数或者成员变量,这个时候如果前面没有 typename,编译器没有任何办法知道 T::LengthType 是一个类型还是一个成员名称(静态数据成员或者静态函数),所以编译不能够通过。
#include
#include
int main() {
printf("std::this_thread::get_id():%d\n", std::this_thread::get_id());
return 0;
}
.\a.cpp: In function 'int main()':
.\a.cpp:5:52: error: 'std::this_thread' has not been declared
5 | printf("std::this_thread::get_id():%d\n", std::this_thread::get_id());
与上面同样的代码
运行:
std::this_thread::get_id():10884
note: ‘std::thread’ is defined in header ‘’; did you forget to '#include '?
而且include的文件都是一样的
另一个是:identifier “thread” is undefined
它的解决方法是:
我的解决办法是:直接使用VS来编写就没有问题的。(编译器不一样,VS中我使用的是默认的:MSVC编译器)
参考 微软在线文档:
C++ std::thread
C++11 原子类型与原子操作
这些概念在很久很久以前我用C#写多线程都是一样的概念。
atomic 就是原子性的操作:原子操作是在多线程程序中“最小的且不可并行化的”操作,意味着多个线程访问同一个资源时,有且仅有一个线程能对资源进行操作。
C#、C++也都封装了原子操作的简便操作。
C#中的我记得是:Interlock_XXX。
C++的则可以直接声明使用原型操作的数据类型:
std::atomic
// jave.lin
#include
#include
#include
#include
// ======================== sleep ========================
// 下面是我们自定义的秒、毫秒、时的时长定义
// C++11 std::chrono库详解
// https://www.cnblogs.com/zlshmily/p/10058427.html
typedef std::chrono::duration<size_t> SecType;
typedef std::chrono::duration<int, std::milli> MSecType; //milliseconds_type;
//typedef std::chrono::duration> HourType; // hours_type;
// 其实也可以在 chrono、ratio头文件中看到类似定义
#if THIS_IS_COMMENT
// in file
// duration TYPES
using nanoseconds = duration<long long, nano>;
using microseconds = duration<long long, micro>;
using milliseconds = duration<long long, milli>;
using seconds = duration<long long>;
using minutes = duration<int, ratio<60>>;
using hours = duration<int, ratio<3600>>;
// in file
// SI TYPEDEFS
using atto = ratio<1, 1000000000000000000LL>;
using femto = ratio<1, 1000000000000000LL>;
using pico = ratio<1, 1000000000000LL>;
using nano = ratio<1, 1000000000>;
using micro = ratio<1, 1000000>;
using milli = ratio<1, 1000>;
using centi = ratio<1, 100>;
using deci = ratio<1, 10>;
using deca = ratio<10, 1>;
using hecto = ratio<100, 1>;
using kilo = ratio<1000, 1>;
using mega = ratio<1000000, 1>;
using giga = ratio<1000000000, 1>;
using tera = ratio<1000000000000LL, 1>;
using peta = ratio<1000000000000000LL, 1>;
using exa = ratio<1000000000000000000LL, 1>;
#endif
void thread_callback_sec(size_t sec) {
printf("-->enter [%s-%d]\n", __func__, std::this_thread::get_id());
// code here
printf("[%s] sleep %d second.\n", __func__, sec);
SecType howManySeconds(sec);
std::this_thread::sleep_for(howManySeconds);
printf("<--exit [%s-].\n", __func__, std::this_thread::get_id());
}
void thread_callback_msec(size_t msec) {
printf("-->enter [%s-%d]\n", __func__, std::this_thread::get_id());
// code here
printf("[%s] sleep %d milli-second.\n", __func__, msec);
MSecType howManyMilliSeconds(msec);
std::this_thread::sleep_for(howManyMilliSeconds);
printf("<--exit [%s-].\n", __func__, std::this_thread::get_id());
}
// ======================== atmoic/synic ========================
// 是否启用线程数据安全同步操作
#define SYNC_ENDABLE
// 同步操作类型,用atomic或是Mutex
#define SYNC_BY_MUTEX 1 // mutex方式
#define SYNC_BY_ATOMIC_DATATYPE 2 // 方式
#define SYNC_TYPE SYNC_BY_ATOMIC_DATATYPE // 同步类型
// 是否启用检测线程结束
#define THREAD_CHECK_END_ENABLED
// 检测线程结束的类型
#define THREAD_CHECK_END_BY_MUTEX 1 // mutex方式
#define THREAD_CHECK_END_BY_ATOMIC_FLAG 2 // atomic_flag方式
#define THREAD_CHECK_END_TYPE THREAD_CHECK_END_BY_ATOMIC_FLAG
// 提前放弃所有线程继续执行
//#define EARLY_ABORT_ALL_THREAD_ENABLE
bool ready = false;
const size_t thread_count = 10;
const size_t thread_loop_count = 100000;
#if defined(SYNC_ENDABLE) && defined(SYNC_TYPE) && SYNC_TYPE == SYNC_BY_ATOMIC_DATATYPE
std::atomic_size_t counter = 0;
#else
size_t counter = 0;
#endif
// diy locker
// 使用std::atomic_flag的原子操作来处理
// std::atomic_flag::test_and_set()第一次返回false,直到clear后才返回true
class mylocker {
public:
// 成功锁定返回
const bool lock() {
while (flag.test_and_set(std::memory_order::memory_order_acquire))
std::this_thread::yield();
return true;
}
void unlock() {
flag.clear(std::memory_order::memory_order_release);
}
private:
std::atomic_flag flag = ATOMIC_FLAG_INIT;
};
#if THREAD_CHECK_END_TYPE == THREAD_CHECK_END_BY_MUTEX
typedef std::mutex locker;
#else // THREAD_CHECK_END_TYPE == THREAD_CHECK_END_BY_ATOMIC_FLAG
typedef mylocker locker;
#endif
class ThreadParams {
public:
locker* group_mutex = nullptr;
locker* add_counter_mutex = nullptr;
ThreadParams()
: group_mutex(nullptr),
add_counter_mutex(nullptr) {
}
ThreadParams(locker* gm, locker* acm)
: group_mutex(gm),
add_counter_mutex(acm) {
}
ThreadParams& operator=(ThreadParams v) {
this->group_mutex = v.group_mutex;
this->add_counter_mutex = v.add_counter_mutex;
return *this;
}
};
void thread_callback_counter(ThreadParams* params) {
while (!ready) {
std::this_thread::yield(); // 放弃后续时间片
}
#if defined(THREAD_CHECK_END_ENABLED)
params->group_mutex->lock();
#endif
printf("-->enter [%s-%d], counter : %ld\n", __func__, std::this_thread::get_id(), (size_t)counter);
for (size_t i = 0; i < thread_loop_count; i++) { // 越快的CPU,这个loop设置大一些,否则看不到效果
#ifdef SYNC_ENDABLE
#if SYNC_TYPE == SYNC_BY_MUTEX
data->add_counter_mutex->lock();
++counter;
data->add_counter_mutex->unlock();
#else
++counter; // 使用atomic的原子操作,不用mutex互斥量来原子操作
#endif
#else
++counter; // 没有原子操作,在多线程并行对同一个数据对象处理会不安全
#endif
}
printf("<--exit [%s-], counter : %ld\n", __func__, std::this_thread::get_id(), (size_t)counter);
#if defined(THREAD_CHECK_END_ENABLED)
params->group_mutex->unlock();
#endif
}
int main() {
printf("support thread : %s\n", __STDCPP_THREADS__ == 1 ? "true" : "false");
printf("using locker type : %s\n", typeid(locker).name());
printf("\n============= join/sleep test ============\n");
printf("[%s-%d]\n", __func__, std::this_thread::get_id());
#if __STDCPP_THREADS__
size_t seconds = 2;
size_t mseconds = 500;
std::thread secThread(thread_callback_sec, seconds);
std::thread::id id = secThread.get_id();
printf("secThread.get_id():%d\n", id);
secThread.join();
std::thread msecThread(thread_callback_msec, mseconds);
id = msecThread.get_id();
printf("msecThread.get_id():%d\n", id);
msecThread.join();
printf("\n============= counter test ============\n");
ThreadParams thParams[thread_count];
locker* add_counter_mutex = new locker();
for (size_t i = 0; i < thread_count; i++) {
//thParams[i] = ThreadParams(new locker(), add_counter_mutex);
thParams[i] = { new locker(), add_counter_mutex };
}
std::thread ts[thread_count];
for (size_t i = 0; i < thread_count; i++) {
ts[i] = std::thread(thread_callback_counter, &thParams[i]);
ts[i].detach(); // 独立运行,join()是线程阻塞执行,有想想sequence执行,detach就并行的
}
// 启用线程结束检测
#ifdef THREAD_CHECK_END_ENABLED
printf("\n============= check exit_group_mutex to exit ============\n");
ready = true;
#ifdef EARLY_ABORT_ALL_THREAD_ENABLE
// 提前放弃、结束线程
MSecType sleepMSecond(1);
printf("sleep %d milliseconds before abort all thread\n.", sleepMSecond);
std::this_thread::sleep_for(sleepMSecond);
printf("after sleep %d seconds abort all thread\n.", sleepMSecond);
for (size_t i = 0; i < thread_count; i++) {
thParams[i].group_mutex->unlock();
ts[i].~thread();
}
add_counter_mutex->unlock();
#endif
// 保证 main thread 最后退出,先让其他子线先执行完,用mutex来检测
for (size_t i = 0; i < thread_count; i++) {
thParams[i].group_mutex->lock();
thParams[i].group_mutex->unlock();
}
// 检测所有线程执行完成的另一个方法更简单的是:
// 在全局变量声明定义一个:atomic_size_t thread_runing_counter = thread_count;
// 然后在每一个线程最后一个语句执行:--thread_runing_counter即可
// 在这里就只要检测:while(thread_runing_counter>0) std::this_thread::yield();即可
#endif // #ifdef THREAD_CHECK_END_ENABLED
printf("\n============= check exit_group_mutex to exit complete! ============\n");
printf("counter:%d\n", (size_t)counter);
#ifndef EARLY_ABORT_ALL_THREAD_ENABLE
printf("thread safely : %s", ((size_t)counter) == (thread_loop_count * thread_count) ? "true" : "false");
#endif // !EARLY_ABORT_ALL_THREAD_ENABLE
#else
printf("not support thread.\n");
#endif
return 0;
}
输出:
support thread : true
using locker type : class mylocker
============= join/sleep test ============
[main-11844]
secThread.get_id():12920
-->enter [thread_callback_sec-12920]
[thread_callback_sec] sleep 2 second.
<--exit [thread_callback_sec-].
msecThread.get_id():21880
-->enter [thread_callback_msec-21880]
[thread_callback_msec] sleep 500 milli-second.
<--exit [thread_callback_msec-].
============= counter test ============
============= check exit_group_mutex to exit ============
-->enter [thread_callback_counter-23256], counter : 0
-->enter [thread_callback_counter-16000], counter : 980
-->enter [thread_callback_counter-16392], counter : 0
-->enter [thread_callback_counter-13912], counter : 0
-->enter [thread_callback_counter-4488], counter : 0
-->enter [thread_callback_counter-9748], counter : 0
-->enter [thread_callback_counter-17864], counter : 0
-->enter [thread_callback_counter-12572], counter : 0
-->enter [thread_callback_counter-19556], counter : 0
-->enter [thread_callback_counter-20844], counter : 0
<--exit [thread_callback_counter-], counter : 16392
<--exit [thread_callback_counter-], counter : 20844
<--exit [thread_callback_counter-], counter : 4488
<--exit [thread_callback_counter-], counter : 9748
<--exit [thread_callback_counter-], counter : 12572
<--exit [thread_callback_counter-], counter : 13912
<--exit [thread_callback_counter-], counter : 23256
<--exit [thread_callback_counter-], counter : 16000
<--exit [thread_callback_counter-], counter : 17864
<--exit [thread_callback_counter-], counter : 19556
============= check exit_group_mutex to exit complete! ============
counter:1000000
thread safely : true
如果我们将#define SYNC_ENDABLE
注释掉。
那么输出counter
的分线程并行计算就没有线程安全了
============= check exit_group_mutex to exit ============
-->enter [thread_callback_counter-8560], counter : 0
-->enter [thread_callback_counter-11232], counter : 0
-->enter [thread_callback_counter-5792], counter : 0
-->enter [thread_callback_counter-3244], counter : 0
-->enter [thread_callback_counter-9656], counter : 55058
<--exit [thread_callback_counter-], counter : 8560
-->enter [thread_callback_counter-14428], counter : 134866
-->enter [thread_callback_counter-21476], counter : 196042
<--exit [thread_callback_counter-], counter : 11232
-->enter [thread_callback_counter-6412], counter : 212773
-->enter [thread_callback_counter-21468], counter : 268226
-->enter [thread_callback_counter-14016], counter : 277952
<--exit [thread_callback_counter-], counter : 5792
<--exit [thread_callback_counter-], counter : 3244
<--exit [thread_callback_counter-], counter : 21468
<--exit [thread_callback_counter-], counter : 9656
<--exit [thread_callback_counter-], counter : 21476
<--exit [thread_callback_counter-], counter : 6412
<--exit [thread_callback_counter-], counter : 14428
<--exit [thread_callback_counter-], counter : 14016
============= check exit_group_mutex to exit complete! ============
counter:447351
thread safely : false
C++ 域有:全局域、命名空间域、类域、函数域、函数子块域
// jave.lin - testing scope
#include
#include
// 全局域
int global_scope_int = 300;
// my_ns1 命名空间域
namespace my_ns1 {
// 命名空间域下也可以定义:成员地段、函数、类、结构、枚举、等
int my_ns_int = 100;
}
// my_ns2 命名空间域
namespace my_ns2 {
int my_ns_int = 200;
}
// 类域
class Test {
public:
static int static_int; // 声明,类静态成员是属于类域
public:
Test(std::string name) : name(name) {
std::cout << "Test ctor : " << name.c_str() << "\n";
}
~Test() {
std::cout << "Test desctor : " << name.c_str() << "\n";
}
private:
std::string name;
};
// static 定义
int Test::static_int = 99;
int main() {
std::cout << "global_scope_int : " << global_scope_int << "\n"; // 可以直接访问全局域
std::cout << "::global_scope_int : " << ::global_scope_int << "\n"; // 如果使用 :: 开头的全局域,再可读性上会更高
// 也可以访问名字空间域下的内容
// 虽然变量是一样的,但在不同的 namepspace 下
std::cout << "my_ns1::my_ns_int : " << my_ns1::my_ns_int << "\n";
std::cout << "my_ns1::my_ns_int : " << my_ns1::my_ns_int << "\n";
// 函数域
// 这里是 main 函数的作用与
Test first("first 11111");
{
// 这里是 mian 函数的嵌套的其他子作用域
Test two("two 22222");
// 可以调用父级作用域 first
}
{
// 这里是 mian 函数的嵌套的其他子作用域
Test three("three 33333");
// 也可以调用父级作用域 first
// 但不能调用 two,因为他们不用域
}
// four 和 first 是同一个域
Test four("four 44444");
return 0;
}
/* 输出:
global_scope_int : 300
::global_scope_int : 300
my_ns1::my_ns_int : 100
my_ns1::my_ns_int : 100
Test ctor : first 11111
Test ctor : two 22222
Test desctor : two 22222
Test ctor : three 33333
Test desctor : three 33333
Test ctor : four 44444
Test desctor : four 44444
Test desctor : first 11111
*/
C++11朝码夕解: move和forward
c++ 中__declspec 的用法
VSC C++ Debugging Settings
无法解析的外部符号。
这是一个编译错误。
先看 错误ID 提示:LNK == Link,2019是编号。
指的是编译后的链接处理除了问题,链接一般处理的是符号地址的重新调整,因为有些全局对象,或是函数,或是类定义的全局成员、函数中,函数用到别的文件的定义的函数、对象是,或是对象、函数被其他文件用到时,在编译时会先预留一个默认的值,这个值是不正确的,以前是需要手动去调整的,很痛苦,现在有链接器了,就可以帮我们快速、无误的处理这些无聊、易错的工作。它就是想一些用到外部文件定义的对象、函数的地址重新调整。有些是静态链接的还会将文件合并处理,再调整所有相关的对象、函数地址(这些都是符号,它们都有地址)。
所以我们知道了链接器的工作原理,这个编译错误的问题自然就不难理解。
case 1
就是说链接器编译过程中符号都是有的(符号一般就是我们就写声明),如果我们的声明与实现分开的话,有些头文件没有明文include 对象的.cpp,那么这些.cpp文件没有在项目中引入,那么编译器是没有问题的,因为声明是有的,但是链接器就找不到外部符号(对象、函数)的具体实现的地址,所以就会报这个错误。
所以解决方法,就是将这些.cpp包含到你的项目中即可解决。
case 2
如果你有一个.lib静态库,在使用时,一般是需要将其放在我们设置的库目录下的,并且要将这个库的头文件放到include目录。
如果这时,你只将头文件放到了include目录,到时.lib文件没有,或是位置不对,那么也是会有这问报错的。
C++ 实例
判断有符号和无符号的变量或类型[C/C++]
typedef char CHAR;
...
typedef wchar_t WCHAR; // wc, 16-bit UNICODE character
...
typedef _Null_terminated_ CHAR *LPSTR;
...
typedef _Null_terminated_ WCHAR *LPWSTR;
...
WINBASEAPI
_Success_(return != 0 && return < nBufferLength)
DWORD
WINAPI
GetCurrentDirectoryA(
_In_ DWORD nBufferLength,
_Out_writes_to_opt_(nBufferLength, return + 1) LPSTR lpBuffer
);
WINBASEAPI
_Success_(return != 0 && return < nBufferLength)
DWORD
WINAPI
GetCurrentDirectoryW(
_In_ DWORD nBufferLength,
_Out_writes_to_opt_(nBufferLength, return + 1) LPWSTR lpBuffer
);
#ifdef UNICODE
#define GetCurrentDirectory GetCurrentDirectoryW
#else
#define GetCurrentDirectory GetCurrentDirectoryA
#endif // !UNICODE
注意,从定义可以看出来
GetCurrentDirectoryW
的最后一个字符 “W” GetCurrentDirectoryW 第二个接收返回字符串结果的类型是 wchar_t
,宽字符的,16 bits,2 个 bytes的。
GetCurrentDirectoryA
的最后一个字符 “A” GetCurrentDirectoryA 第二个接收返回字符串结果的类型是 char_t
,宽字符的,8 bits,1 个 byte的。
#include
#include
int main() {
char exeFullPath[512];
char combinPath[512];
GetCurrentDirectoryA(1000, exeFullPath);
sprintf(combinPath, "%s\\Debug\\%s", exeFullPath, "shader1.vert");
std::cout << "combinPath : " << combinPath << std::endl;
return 0;
}
我们在window 开始菜单输入:notepad
默认打开的notepad 的窗体标题是:无标题 - 记事本
然后我们可以通过全局 API FindWindow 来查找窗体句柄
直接使用该 API的话,现在是有分两个版本的,一个是宽字节(或叫长字节:typedef unsigned short wchar_t;
),或是普通短字节(typedef char CHAR;
)
WINUSERAPI
HWND
WINAPI
FindWindowA(
_In_opt_ LPCSTR lpClassName,
_In_opt_ LPCSTR lpWindowName);
WINUSERAPI
HWND
WINAPI
FindWindowW(
_In_opt_ LPCWSTR lpClassName,
_In_opt_ LPCWSTR lpWindowName);
#ifdef UNICODE
#define FindWindow FindWindowW
#else
#define FindWindow FindWindowA
#endif // !UNICODE
const wchar_t* _w_chars = L"this is wchars"; // 宽字符串
const char* _chars = "this is normal chars"; // 短字符串
#define USE_WC
#ifdef USE_WC
const wchar_t* winTitleName = L"无标题 - 记事本";
//HWND handle = ::FindWindowW(L"Notepad", winTitleName);
HWND handle = ::FindWindowW(NULL, winTitleName);
#else
const char* winTitleName = L"无标题 - 记事本";
HWND handle = ::FindWindowA("Notepad", winTitleName);
#endif
if (handle != NULL) {
std::cout << "Found the Window : " << winTitleName << "\n";
}
else {
std::cout << "Not Found the Window : " << winTitleName << "\n";
}
glBindTextureUnit(0, 99);
GLenum error = glGetError();
if (error != 0) {
std::cout << std::dec; // 以10进制显示数值
std::cout << "glError : " << error;
std::cout << std::hex; // 以16进制显示数值
std::cout << "(0x" << error << ")" << std::endl;
}
/* 输出:
glError : 1282(0x502)
*/
C++ 实例 - 求一元二次方程的根
二次方程 ax2+bx+c = 0 (其中a≠0),a 是二次项系数,bx 叫作一次项,b是一次项系数;c叫作常数项。
x 的值为:
x = − b ± b 2 − 4 a c 2 a x=\cfrac{-b\pm\sqrt{b^2-4ac}}{2a} x=2a−b±b2−4ac
对于 实系数 一元二次方程 a x 2 + b x + c = 0 ( 0 ) ax^2+bx+c=0(0) ax2+bx+c=0(0), Δ = b 2 − 4 a c \Delta=b^2-4ac Δ=b2−4ac称作一元二次方程根的 判别式。根据判别式,一元二次方程的根有三种可能的情况:
#include
#include
using namespace std;
int main() {
float a, b, c, x1, x2, discriminant, realPart, imaginaryPart;
cout << "输入 a, b 和 c: ";
cin >> a >> b >> c;
discriminant = b*b - 4*a*c;
if (discriminant > 0) {
x1 = (-b + sqrt(discriminant)) / (2*a);
x2 = (-b - sqrt(discriminant)) / (2*a);
cout << "Roots are real and different." << endl;
cout << "x1 = " << x1 << endl;
cout << "x2 = " << x2 << endl;
}
else if (discriminant == 0) {
cout << "实根相同:" << endl;
x1 = (-b + sqrt(discriminant)) / (2*a);
cout << "x1 = x2 =" << x1 << endl;
}
else {
realPart = -b/(2*a);
imaginaryPart =sqrt(-discriminant)/(2*a);
cout << "实根不同:" << endl;
cout << "x1 = " << realPart << "+" << imaginaryPart << "i" << endl;
cout << "x2 = " << realPart << "-" << imaginaryPart << "i" << endl;
}
return 0;
}
以上程序执行输出结果为:
输入 a, b 和 c: 4
5
1
实根不同:
x1 = -0.25
x2 = -1
一个一定要好好学一下
MARS (MIPS Assembler and Runtime Simulator)
MIPS Assembly Programming Simplified
汇编入门(长文多图,流量慎入!!!)
面试题一般是市场需要的知识点。
可以根据需要来学习,会比较快速可投入项目。
C++ 面试知识点总结
c++面试
【腾讯内部工具分享】内存泄漏分析工具tMemoryMonitor
https://en.cppreference.com/w/ - 在线学习C++极力推荐的网站,内容很全
http://www.cplusplus.com/
Visual Studio 中的 C++
C/C++语言和标准库参考
C++标准库头文件
C++ Primer 第三版 中文版.pdf提取码:czse
C++ 教程