跳转至

实用处理技巧

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 无拷贝类的实现

  1. 编写一个基类,将拷贝构造,拷贝复制私有化
  2. 类私有基类上面的类。
class noncopyable
{
protected:
  noncopyable() {}
  ~noncopyable() {}
private:
  noncopyable(const noncopyable&);
  const noncopyable& operator=(const noncopyable&);
};
class A : private noncopyable
{

};