实用处理技巧¶
1 内存泄漏检测方法¶
下面代码中 #define _CRTDBG_MAP_ALLOC
宏作用是让内存泄漏信息更完善,只适合 malloc 分配内存,用 new 不产生额外信息
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
int main()
{
//方法一:在程序开始位置加上 _CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
int* i1 = new int(1);
int* i2 = (int*)malloc(4);
//方法二:在程序结束位置加上 _CrtDumpMemoryLeaks();
_CrtDumpMemoryLeaks();
}
2 如何把函数存进容器中¶
2.1 1.针对函数声明函数指针¶
作用:函数指针是用来作为容器的模板类型 - 假设要定义的函数如下
string* fun1(int)
{
return new string("fun1");
}
- 对应函数指针声明如下
typedef string* (*pf)(int);
2.2 2.定义容器类型¶
注意:容器是函数指针类型
//向量类型
vector<pf> v;
//map类型
map<string, pf> mpf;
2.3 3.将函数名或 lambda 表达式存入容器¶
- map 插入函数方法
//map中插入函数
mpf.insert(make_pair("1", fun1));
//map中插入lambda表达式
mpf.insert(make_pair("3", [](int)->string* {return new string("fun3"); }));
- vector 插入函数方法
v.push_back(fun1);
v.push_back([](int)->string* {return new string("fun3"); });
2.4 4.使用容器类函数¶
- map 中函数使用
if (it != mpf.end)
{
string* pstr = it->second(1);
delete pstr;
}
- vector 中函数使用
v[0](1);
2.5 完整测试代码¶
#include <map>
#include <vector>
#include <string>
using namespace std;
typedef string* (*pf)(int);
vector<pf> v;
map<string, pf> mpf;
string* fun1(int)
{
return new string("fun1");
}
string* fun2(int)
{
return new string("fun2");
}
int setFun()
{
v.push_back(fun1);
v.push_back([](int)->string* {return new string("fun3"); });
mpf.insert(make_pair("1", fun1));
mpf.insert(make_pair("2", fun2));
mpf.insert(make_pair("3", [](int)->string* {return new string("fun3"); }));
return 1;
}
int main(int argc, char* argv[])
{
setFun();
map<string, pf>::iterator it= mpf.find("3");
if (it != mpf.end)
{
string* pstr = it->second(1);
delete pstr;
}
v[0](1);
}
3 如何设计一个类仅有一个实例¶
4 C++ 可以在头文件中放全局变量吗¶
在 C++中,通常不建议在头文件中直接放置全局变量的定义,因为这会导致多个源文件包含该头文件时出现重复定义的问题。全局变量的定义应该放在一个源文件中,并在头文件中使用 extern
关键字进行声明。
4.1 全局变量的定义和声明¶
- 定义:全局变量的定义应该放在一个源文件中。例如,在
global.cpp
中:
// global.cpp
int globalVariable = 42; // 定义全局变量
- 声明:在头文件中使用
extern
关键字声明全局变量。例如,在global.h
中:
// global.h
extern int globalVariable; // 声明全局变量
4.2 使用全局变量¶
在其他源文件中,可以通过包含头文件来使用这个全局变量:
// main.cpp
#include "global.h"
int main() {
globalVariable = 100; // 使用全局变量
return 0;
}
4.3 为什么不在头文件中定义全局变量?¶
如果在头文件中直接定义全局变量:
// global.h
int globalVariable = 42; // 错误的定义方式
那么当多个源文件包含这个头文件时,每个源文件都会包含这个定义,导致链接时出现重复定义的错误。
4.4 静态全局变量¶
如果你希望全局变量的作用域仅限于一个编译单元(即一个源文件),可以使用 static
关键字:
// global.cpp
static int globalVariable = 42; // 静态全局变量,仅在 global.cpp 中可见
在这种情况下,globalVariable
在其他源文件中是不可见的。
4.5 总结¶
- 不要在头文件中定义全局变量。
- 在源文件中定义全局变量,并在头文件中使用
extern
声明。 - 如果需要限制全局变量的作用域,可以使用
static
关键字。
5 判断文本编码格式¶
5.1 utf-8 格式表示字符范围¶
Unicode/UCS-4 | bit 数 | UTF-8 | byte 数 |
---|---|---|---|
0000~007F | 0~7 | 0XXX XXXX | 1 |
0080~07FF | 8~11 | 110X XXXX 10XX XXXX | 2 |
0800~FFFF | 12~16 | 1110XXXX 10XXXXXX 10XXXXXX | 3 |
10000~1FFFFF | 17~21 | 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX | 4 |
5.2 利用 utf-8 编码特性来判断文本是否是 utf-8 编码¶
bool IsUTF8(const void* pBuffer, long size)
{
bool IsUTF8 = true;
unsigned char* start = (unsigned char*)pBuffer;
unsigned char* end = (unsigned char*)pBuffer + size;
while (start < end)
{
if (*start < 0x80) // (10000000): 值小于0x80的为ASCII字符
{
start++;
}
else if (*start < (0xC0)) // (11000000): 值介于0x80与0xC0之间的为无效UTF-8字符
{
IsUTF8 = false;
break;
}
else if (*start < (0xE0)) // (11100000): 此范围内为2字节UTF-8字符
{
if (start >= end - 1)
{
break;
}
if ((start[1] & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
start += 2;
}
else if (*start < (0xF0)) // (11110000): 此范围内为3字节UTF-8字符
{
if (start >= end - 2)
{
break;
}
if ((start[1] & (0xC0)) != 0x80 || (start[2] & (0xC0)) != 0x80)
{
IsUTF8 = false;
break;
}
start += 3;
}
else
{
IsUTF8 = false;
break;
}
}
return IsUTF8;
}
bool CConvertCharset::IsUTF8File(const char* pFileName)
{
FILE *f = NULL;
fopen_s(&f, pFileName, "rb");
if (NULL == f)
{
return false;
}
fseek(f, 0, SEEK_END);
long lSize = ftell(f);
fseek(f, 0, SEEK_SET); //或rewind(f);
char *pBuff = new char[lSize + 1];
memset(pBuff, 0, lSize + 1);
fread(pBuff, lSize, 1, f);
fclose(f);
bool bIsUTF8 = IsUTF8Text(pBuff, lSize);
delete []pBuff;
pBuff = NULL;
return bIsUTF8;
}
5.3 通过检查文件的前几个字节(BOM)来判断常见的编码格式¶
判断文件是否包含 BOM(Byte Order Mark)可以通过检查文件的前几个字节来实现。不同的编码格式有不同的 BOM 字节序列。以下是几种常见编码格式的 BOM 字节序列:
- UTF-8:
0xEF 0xBB 0xBF
- UTF-16 (Big Endian):
0xFE 0xFF
- UTF-16 (Little Endian):
0xFF 0xFE
- UTF-32 (Big Endian):
0x00 0x00 0xFE 0xFF
- UTF-32 (Little Endian):
0xFF 0xFE 0x00 0x00
以下是一个 C++ 示例代码,用于判断文件是否包含 BOM:
#include <iostream>
#include <fstream>
#include <vector>
enum class Encoding {
UTF8,
UTF16BE,
UTF16LE,
UTF32BE,
UTF32LE,
Unknown
};
Encoding detect_bom(const std::string& filename) {
std::ifstream file(filename, std::ios::binary);
if (!file) {
throw std::runtime_error("Failed to open file");
}
std::vector<char> buffer(4);
file.read(buffer.data(), 4);
if (buffer[0] == '\xEF' && buffer[1] == '\xBB' && buffer[2] == '\xBF') {
return Encoding::UTF8;
} else if (buffer[0] == '\xFE' && buffer[1] == '\xFF') {
return Encoding::UTF16BE;
} else if (buffer[0] == '\xFF' && buffer[1] == '\xFE') {
return Encoding::UTF16LE;
} else if (buffer[0] == '\x00' && buffer[1] == '\x00' && buffer[2] == '\xFE' && buffer[3] == '\xFF') {
return Encoding::UTF32BE;
} else if (buffer[0] == '\xFF' && buffer[1] == '\xFE' && buffer[2] == '\x00' && buffer[3] == '\x00') {
return Encoding::UTF32LE;
} else {
return Encoding::Unknown;
}
}
int main() {
try {
std::string filename = "example.txt";
Encoding encoding = detect_bom(filename);
switch (encoding) {
case Encoding::UTF8:
std::cout << "File has UTF-8 BOM" << std::endl;
break;
case Encoding::UTF16BE:
std::cout << "File has UTF-16 (Big Endian) BOM" << std::endl;
break;
case Encoding::UTF16LE:
std::cout << "File has UTF-16 (Little Endian) BOM" << std::endl;
break;
case Encoding::UTF32BE:
std::cout << "File has UTF-32 (Big Endian) BOM" << std::endl;
break;
case Encoding::UTF32LE:
std::cout << "File has UTF-32 (Little Endian) BOM" << std::endl;
break;
case Encoding::Unknown:
std::cout << "File does not have a BOM" << std::endl;
break;
}
} catch (const std::exception& e) {
std::cerr << e.what() << std::endl;
}
return 0;
}
6 无拷贝类的实现¶
- 编写一个基类,将拷贝构造,拷贝复制私有化
- 类私有基类上面的类。
class noncopyable
{
protected:
noncopyable() {}
~noncopyable() {}
private:
noncopyable(const noncopyable&);
const noncopyable& operator=(const noncopyable&);
};
class A : private noncopyable
{
};