1 关键字
inline :调用Inline函数直接嵌入代码,避免因为调用而造成的开销。
注意,内联函数应该放入头文件中。
extern :使用时候,也必须声明,应用应在cpp文件中。
const :
A.h中定义的全局变量int a;能被别的cpp文件访问,只要extern int a;
如果不想被访问,则定义成const int a;
&引用:
引用是一种复合类型,必须用对象初始化,是对象的别名。
int & i= 10;error
int & i= j;right
const &是指向const对象的引用。
const int i= 10;
const int & j= i;right
int &k = i; error
typedef 类型或结构 别名
static :是静态对象。即使函数退出,该变量仍然未销,直到程序退出。
2 系统函数
atoi :const char*转int形,window下可用
atol :const char*转long形,window下可用
sprintf : linux下可用这个函数进行转换成字符数组,但是,不能转成string类型。
可先转成char s[],进而转成string。
ntohl
ntohs
htonl
memchr
strchr查找char*中的某个字符位置
strstr 查找char*中的某个char*位置
memcmp 比较char*
memcpy char*拷贝
memmove char*移动
memset char*按位设置字符
流输入
#include
stringstream os;
int i=1001;
string str;
os << i << “.hello”;
os >> str;
//os.str();
strtok 分割字符串:char *strtok(char *s, const char *delim); 分解字符串为一组字符串。s为要分解的字符串,delim为分隔符字符串。
3 初级编程
3.1网络字节序对齐
27 #pragma pack(push,1)
28 typedef struct JsonHeader
29 {
30 uint32_t m_len;
31 uint16_t m_cmd;
32 uint32_t m_seq;
33 }JSONHEADER;
34 #pragma pack(pop)
3.2 预处理
#ifndef _HELLO_
#define _HELLO_
//头文件,变量,函数声明
#endif
3.3 函数返回值
函数返回对象的引用,没有复制返回值。
禁止返回局部变量的引用。
const string & fun(const string &s)
{
string s2 = s;
return s2;//error,函数执行完毕后释放s2
return s;
}
3.4 函数指针和回调函数
函数指针是指向函数的指针变量。 因而“函数指针”本身首先应是指针变量,只不过该指针变量指向函数。这正如用指针变量可指向整型变量、字符型、数组一样,这里是指向函数。如前所述,C在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针所指向的地址。有了指向函数的指针变量后,可用该指针变量调用函数,就如同用指针变量可引用其他类型变量一样,在这些概念上是一致的。函数指针有两个用途:调用函数和做函数的参数。
int func(int x);
int (*f) (int x);
f=func;
赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址
例子:
//返回值是一个引用
const int & max(const int &x,const int &y)
{
return (x>y?x:y);
}
const int & (*ptr)(const int &,const int & );
int fun_point_test()
{
int x = 1;
int y = 2;
ptr = max;//对指针赋初值
cout<<"max = "<<(*ptr)(x,y)<<endl;
cout<<"max = "<<max(x,y)<<endl;
return 0;
}
3.5 值传递
值传递:函数不会访问当前调用的实参,函数处理的值是它本地的一个拷贝,这些拷贝被保存在栈中,一旦函数退出,它们就会消失。参数实际值不会改变。
值传递不适合大型类对象作为参数,拷贝开销太大。
引用传递:别名,指向同一块内存空间。
想用引用又不想改变其值,可以定义成const A&
3.6 拷贝构造函数
当类中有指针成员变量和new开辟新的空间时候,必须使用拷贝构造函数。
A{private:int *p;
public:A(const A&);p = new int[10];}
5 class A 6 { 7 public: 8 A() { cout << "this is gouzao fun!" << endl; } 9 A(const A &) { cout << "this is copy gouzao fun!" << endl; } 10 A & operator =(const A &a) { cout << "this is fuzhi gouzao fun!" << endl; } 11 ~A(){} 12 }; 13 14 void class_test() 15 { 16 A A1; 17 A A2(A1); 18 A A3 = A2; 19 A3 = A1; 20 }; Cout>>>>>> this is gouzao fun! this is copy gouzao fun! this is copy gouzao fun! this is fuzhi gouzao fun! |
3.7虚函数、纯虚函数、抽象类、虚析构函数
虚函数定义在基类中,实现也可以在基类中,如果子类没有同名函数,则仍然用基类中的虚函数。
纯虚函数定义在基类中,实现必须在子类中,是一种多态的体现。
具有纯虚函数的类称抽象类,不能实例化对象,必须有子类继承,实现其纯虚函数。
虚析构函数不能随便声明,只有在class作为多态的基类时,适合将基类的析构函数声明成虚析构函数。
Class Base { Virtual ~Base(){} }
Class A:public base { Public: A(){} ~A() { If(p) Delete [] p; } Init() { P = new Char[10]; } Private: Char *p; }
Int main() { Base *pBase = new A; Delete pBase; } //不会调运A类的析构函数去释放空间 //所以要用虚析构函数 virtual ~A() { If(p) Delete [] p; } |
3.8链接库
静态链接库
生成静态lib
#ifdef _STATIC_LIB_H_
#define _STATIC_LIB_H_
#ifdef __cplusplus
extern "C" {
#endif
int add(int a, int b);
int subtract(int a, int b);
#ifdef __cplusplus
{
#endif
#endif
--------------------------------------
#include"StaticLib.h"
int add(int a, int b){
return a+b;
}
int subtract(int a, int b){
return a-b;
}
使用
将.lib和.h拷到目录下。
#include
#include"StaticLib.h"
#pragma comment(lib,"StaticLib.lib")
int main (){
int a=3,b=4;
int c=0;
c=add(a,b);
printf("%d+%d=%d",a,b,c );
c=subtract(a,b);
printf("%d-%d=%d",a,b,c );
//return 0;
}
动态链接库
一,生成动态链接库
//DynamicLib.h
#ifndef _DYNAMIC_LIB_INCLUDE_H_
#define _DYNAMIC_LIB_INCLUDE_H_
#ifdef __cplusplus
extern"C"{
#endif
_declspec(dllexport) int add(int a,int b);
_declspec(dllexport) int sub(int a,int b);
#ifdef __cplusplus
};
#endif
#endif
------------------------------------------------------
//DynamicLib.cpp
#include "DynamicLib.h"
int add(int a,int b){
return a+b;
}
int sub(int a,int b){
return a-b;
}
---------------------------------------------------
生存动态链接库,得到两个文件如下:
DynamicLib.dll
DynamicLib.lib
使用动态链接库
2.1隐式使用
#include "DynamicLib.h"
#pragma comment(lib,"DynamicLib.lib")
int main(){
int a=10; int b=20; int c=0;
c = add(a,b);
printf("%d+%d=%d\n",a,b,c);
c = sub(a,b);
printf("%d-%d=%d\n",a,b,c);
return 0;
}
2.2显示使用
#include
//#include "DynamicLib.h"
#include
//#pragma comment(lib,"DynamicLib.lib")
typedef int(*pADD)(int a,int b);
typedef int(*pSUB)(int a,int b);
int main(){//显示使用动态链接库
HINSTANCE hModule = ::LoadLibrary("DynamicLib.dll"); //句柄
pADD add = (pADD)::GetProcAddress(hModule,"add");
pSUB sub = (pSUB)::GetProcAddress(hModule,"sub");
int a=10,b=20,c=0;
c = add(a,b);
printf("%d+%d=%d\n",a,b,c);
c = sub(a,b);
printf("%d-%d=%d\n",a,b,c);
::FreeLibrary(hModule);
return 0;
}
4 C++工程规范
1).h文件定义函数和变量
.cpp实现
2)文件名小写全拼
类名首字母大写pascal规则
3)尽可能用const定义变量和函数
4)必须进行返回值判断。
3.9 GDB
valgrind --leak-check=full ./
if(it->second)
171 {
172 if(it->second->vec145data.size() != 15)
173 {
174 cout <<"map145bacdata vec145data size = " << it->second->vec145data.size() <<endl;
175 assert(0);//程序强制在这个地方出core
176 }
177 }
3.10 STL
1. 循环语句删除map中的元素,应将it++放在删除的地方
map145bacdata->erase(it++);
it++不能放在for体里面。
2. vector不完全排序
bool UDgreater ( DataInfo elem1, DataInfo elem2 )
return elem1.dzf > elem2.dzf;
int sortsize(5);
partial_sort(vecInfo.begin(), vecInfo.begin() + sortsize, vecInfo.end(), UDgreater);//不完全排序,stl中的方法
3. vector完美删除
3.11 指针
指针的应用应该注意,在什么时候new出来的地方,一定要注意delete,否则会造成内存泄露,程序崩溃。
可以用C++的delete释放,也可以用自己编写的释放内存类释放。
特别是结构体内的指针释放,应该注意。
4 高级编程
4.1模板template
template
void ClearAndFreeMap(map *needfreemap)
{
typename map::iterator it;
for(it = needfreemap->begin(); it != needfreemap->end(); it++)
{
if(it->second)
{
delete it->second;
it->second = NULL;
}
}
needfreemap->clear();
}
4.2 map结构体key用法
struct StkMapKey { int64_t dtime; // C1 int64_t seq; // C2 string obj; // CO
bool operator <(const StkMapKey& other) const // 注意是const函数 { if (dtime != other.dtime) // dtime按升序排序 { return (dtime < other.dtime); } else if(strcmp(obj.c_str(), other.obj.c_str()) != 0) { return (strcmp(obj.c_str(), other.obj.c_str()) < 0); } else //if(seq != other.seq) // 如果dtime相同,按seq升序排序 { return (seq < other.seq); } } }; |
5 代码优化
5.1 变量
(1) 变量在哪儿使用,才开始定义,这点和C语言编程风格不同。
(2) 常量用const修饰。
(3) 静态变量 s_
(4) 全局变量 g_
(5) 类成员变量 m_
5.2 好的编程习惯
(1) “,”后面空一格,赋值、比较等符号前后各空一格。
(2) 函数、类等每个逻辑之间空一行。
(3) 赋值语句作为变量时,应将赋值语句加括号。
(4) 指针必须在使用完后释放。
(5) 重复代码应采取适当的方法,将代码合并。
(6) 巧用os进行任意类型的转换。
#include
os << “str”;
os.str().c_str();//返回const char *
(7) 类的成员变量声明顺序和类的构造函数对成员变量的初始化顺序应保持一致。
(8) * & 等修饰符号 靠近数据。
int *i, j;
(9) 注释规范。
(10)if——else——
if (condition)
return x;
return y;
改成
return (condition ? x : y);
(11) 多重循环,长循环在外层,程序效率较高。
(12) 断言 assert 是仅在Debug 版本起作用的宏,它用于检查“不应该”发生的情况。
在调试结束后,可以通过在包含#include 的语句之前插入 #define NDEBUG 来禁用assert调用。
(13) 内联inline函数的声明和定义都在一起。内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销
(14) 把对象的初始化工作放在构造函数中,把清除工作放在析构函数
中。当对象被创建时,构造函数被自动执行。当对象消亡时,析构函数被自动执行。这
下就不用担心忘了对象的初始化和清除工作。
(15) 拷贝构造函数和赋值函数。
这是在创建类的对象的时候,给对象初始化的两种特殊的构造函数。
string a(“hello”);
string b(a); //拷贝构造函数
string c; c = a; //赋值函数,指向同一块地址
(16) 引用传递的原则:对于非内部数据类型的输入参数,应该将“值传递”的方式改为“const 引用传递”,目的是提高效率。例如将void Func(A a) 改为void Func(const A &a)。对于内部数据类型的输入参数,不要将“值传递”的方式改为“const 引用传递”。
否则既达不到提高效率的目的,又降低了函数的可理解性。例如void Func(int x) 不
应该改为void Func(const int &x)。
用const 修饰函数的返回值 如果给以“指针传递”方式的函数返回值加const 修饰,那么函数返回值(即指针)的内容不能被修改,该返回值只能被赋给加const 修饰的同类型指针。