C++小白最容易踩的10个坑(附避坑指南)

举报
码事漫谈 发表于 2025/11/28 19:19:46 2025/11/28
【摘要】 作为初学者,C++的复杂性常常让人头疼。下面这些错误几乎每个C++新手都会遇到,来看看你中招了几个? 1. 内存管理:野指针和内存泄漏 常见错误代码:// 错误示例1:野指针int* ptr = new int(42);delete ptr;cout << *ptr; // 危险!ptr已经成为野指针// 错误示例2:内存泄漏void createMemoryLeak() { int...

作为初学者,C++的复杂性常常让人头疼。下面这些错误几乎每个C++新手都会遇到,来看看你中招了几个?

1. 内存管理:野指针和内存泄漏

常见错误代码:

// 错误示例1:野指针
int* ptr = new int(42);
delete ptr;
cout << *ptr;  // 危险!ptr已经成为野指针

// 错误示例2:内存泄漏
void createMemoryLeak() {
    int* ptr = new int[100];
    // 忘记 delete[] ptr;
    return;  // 内存泄漏!
}

正确做法:

// 方案1:使用智能指针(C++11+)
#include <memory>
auto ptr = std::make_unique<int>(42);  // 自动管理内存

// 方案2:RAII原则
class ResourceHolder {
private:
    std::unique_ptr<int[]> data;
public:
    ResourceHolder(size_t size) : data(std::make_unique<int[]>(size)) {}
    // 析构函数自动调用,内存自动释放
};

2. 字符串操作陷阱

常见错误:

// 错误:缓冲区溢出
char str[10];
strcpy(str, "This string is too long!");  // 缓冲区溢出!

// 错误:字符串比较
char str1[] = "hello";
char str2[] = "hello";
if (str1 == str2) {  // 比较的是地址,不是内容!
    // 永远不会执行
}

正确做法:

// 使用std::string代替C风格字符串
#include <string>
std::string str = "hello";
std::string str2 = "hello";

if (str == str2) {  // 正确比较内容
    // 会执行
}

// 或者安全的C字符串操作
char str[20];
strncpy(str, "This string is too long!", sizeof(str) - 1);
str[sizeof(str) - 1] = '\0';  // 确保以null结尾

3. 对象切片(Object Slicing)

常见错误:

class Animal {
public:
    virtual void speak() { cout << "Animal sound" << endl; }
};

class Dog : public Animal {
public:
    void speak() override { cout << "Woof!" << endl; }
};

void makeSound(Animal animal) {  // 按值传递
    animal.speak();  // 总是输出 "Animal sound"
}

Dog dog;
makeSound(dog);  // 对象被切片,多态失效

正确做法:

// 使用引用或指针传递
void makeSound(Animal& animal) {  // 按引用传递
    animal.speak();  // 正确调用派生类的方法
}

// 或者使用智能指针
void makeSound(std::unique_ptr<Animal> animal) {
    animal->speak();
}

4. 浅拷贝问题

常见错误:

class BadString {
private:
    char* data;
public:
    BadString(const char* str) {
        data = new char[strlen(str) + 1];
        strcpy(data, str);
    }
    
    // 缺少拷贝构造函数和赋值运算符
    // 默认的浅拷贝会导致双重释放!
};

BadString str1("hello");
BadString str2 = str1;  // 灾难!两个对象共享同一块内存

正确做法:

class GoodString {
private:
    char* data;
public:
    // 构造函数
    GoodString(const char* str) {
        data = new char[strlen(str) + 1];
        strcpy(data, str);
    }
    
    // 拷贝构造函数
    GoodString(const GoodString& other) {
        data = new char[strlen(other.data) + 1];
        strcpy(data, other.data);
    }
    
    // 赋值运算符
    GoodString& operator=(const GoodString& other) {
        if (this != &other) {
            delete[] data;
            data = new char[strlen(other.data) + 1];
            strcpy(data, other.data);
        }
        return *this;
    }
    
    // 析构函数
    ~GoodString() {
        delete[] data;
    }
};

5. 数组边界溢出

常见错误:

int arr[5] = {1, 2, 3, 4, 5};

// 错误:访问越界
for (int i = 0; i <= 5; i++) {  // i=5 时越界
    cout << arr[i] << endl;
}

// 错误:sizeof误用
void printArray(int arr[]) {
    // sizeof(arr) 返回的是指针大小,不是数组大小!
    for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
        cout << arr[i] << endl;  // 可能越界
    }
}

正确做法:

// 使用std::array或std::vector
#include <array>
#include <vector>

std::array<int, 5> arr = {1, 2, 3, 4, 5};
for (int i = 0; i < arr.size(); i++) {  // 安全的边界检查
    cout << arr[i] << endl;
}

// 或者使用范围for循环
for (const auto& element : arr) {
    cout << element << endl;
}

6. 未初始化变量

常见错误:

int value;  // 未初始化,值是不确定的
cout << value;  // 可能输出任意值

class MyClass {
    int x;  // 未初始化
public:
    void print() { cout << x; }  // 危险!
};

正确做法:

// 总是初始化变量
int value = 0;
int value{};  // C++11统一初始化

class MyClass {
    int x = 0;  // 成员初始化
public:
    MyClass() = default;
    MyClass(int val) : x(val) {}  // 成员初始化列表
};

7. 函数返回局部变量的引用/指针

常见错误:

// 错误:返回局部变量的引用
int& badFunction() {
    int localVar = 42;
    return localVar;  // 局部变量会被销毁!
}

// 错误:返回局部变量的指针
char* badString() {
    char localStr[] = "hello";
    return localStr;  // 局部数组会被销毁!
}

正确做法:

// 返回值而不是引用
int goodFunction() {
    int localVar = 42;
    return localVar;  // 返回副本,安全
}

// 或者返回动态分配的内存(但要记得释放)
std::unique_ptr<int> createNumber() {
    return std::make_unique<int>(42);
}

// 或者让调用者提供存储空间
void fillString(char* buffer, size_t size) {
    strncpy(buffer, "hello", size - 1);
    buffer[size - 1] = '\0';
}

8. 混淆=和==

常见错误:

int x = 5;

// 这个bug很难发现!
if (x = 10) {  // 应该是 x == 10
    cout << "x is 10" << endl;  // 总是会执行,而且x被改成了10!
}

正确做法:

// 习惯把常量放在左边
if (10 == x) {  // 如果写成 10 = x,编译器会报错
    cout << "x is 10" << endl;
}

// 或者启用编译器警告
// g++ -Wall -Wextra 会警告这种赋值操作

9. 头文件包含问题

常见错误:

// 头文件 guard 忘记写
// myclass.h
class MyClass {
    // ...
};  // 如果没有头文件guard,可能重复定义

// 循环包含
// a.h
#include "b.h"
// b.h  
#include "a.h"  // 循环包含!

正确做法:

// myclass.h
#ifndef MYCLASS_H  // 头文件guard
#define MYCLASS_H

class MyClass {
    // ...
};

#endif // MYCLASS_H

// 或者使用 #pragma once(大多数编译器支持)
#pragma once
class MyClass {
    // ...
};

10. 异常安全忽略

常见错误:

void unsafeFunction() {
    int* ptr = new int[100];
    someFunctionThatMightThrow();  // 如果这里抛出异常...
    delete[] ptr;  // 这行不会执行,内存泄漏!
}

正确做法:

// 使用RAII确保异常安全
void safeFunction() {
    std::vector<int> data(100);  // 自动管理内存
    someFunctionThatMightThrow();  // 即使抛出异常,data也会自动清理
}

// 或者使用智能指针
void safeFunction2() {
    auto ptr = std::make_unique<int[]>(100);
    someFunctionThatMightThrow();  // 异常安全
}

给C++小白的生存指南

  1. 启用所有编译器警告

    g++ -Wall -Wextra -Wpedantic -std=c++17 program.cpp
    
  2. 优先使用标准库std::vectorstd::stringstd::unique_ptr

  3. 学习RAII原则:资源获取即初始化

  4. 避免裸指针:使用智能指针管理内存

  5. 多写测试:特别是边界情况的测试

  6. 使用现代C++:C++11/14/17/20的特性让很多传统问题变得简单

记住,每个C++高手都曾经是小白,都踩过这些坑。关键是要从错误中学习,理解背后的原理,这样你就能写出更安全、更高效的C++代码!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。