C++ 类和对象基础详解:从封装理解对象生命周期

 更新时间:2026年07月13日 09:37:35   作者:红豆诗人  
这篇文章给大家介绍C++ 类和对象基础详解:从封装理解对象生命周期,本文结合实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧

前言

复习 C++ 类和对象时,我一开始只记住了一个很粗的说法:类就是把变量和函数放到一起。这个说法不能算错,但它只能算入口。

后面再看构造、析构、拷贝这些内容,才发现类和想知道C++类和对象的本质吗?别再死记语法,立即掌握对象生命周期、封装、构造析构与资源管理的核心逻辑,从成员变量为什么放private到拷贝构造的深拷贝陷阱,本文用清晰案例带你理清所有知识点,解决你“会用但不懂”的困惑,直接提升代码设计能力对象的难点不只是 class 怎么写,而是对象在整个生命周期里到底发生了什么。比如:

  • 成员变量为什么一般要放到 private 里?
  • 构造函数和普通 Init 函数到底有什么区别?
  • 为什么有的类不用写析构,有的类必须写?
  • 拷贝构造和赋值重载为什么经常和资源管理绑在一起?
  • 初始化列表、static、友元这些语法分别解决什么问题?

一、类到底在解决什么问题

如果只用 C 语言风格写代码,我们通常会把数据和函数分开。

比如写一个学生信息:

struct Student {
    char name[32];
    int age;
    int score;
};
void PrintStudent(const Student* stu);
void SetScore(Student* stu, int score);

这种写法能用,但数据边界几乎全靠调用者自觉,外部代码可以直接修改结构体里的字段。

Student stu;
stu.age = -10;
stu.score = 999;

从语法上看没有问题,但从业务含义上看明显不合理。年龄不应该是负数,分数也不应该随便越界。

C++ 的类把数据和操作放到一起,并且用访问权限控制“谁能改、怎么改”。理解类时,可以先抓住这一点:

类不是单纯把变量和函数包起来,而是给数据加上使用规则。

这是封装最基础的意义。

二、类的定义、访问限定符和封装

先用一个小类看封装的写法:

#include <iostream>
#include <string>
class Student {
public:
    void SetInfo(const std::string& name, int age, int score) {
        name_ = name;
        age_ = age;
        SetScore(score);
    }
    void SetScore(int score) {
        if (score < 0) {
            score_ = 0;
        } else if (score > 100) {
            score_ = 100;
        } else {
            score_ = score;
        }
    }
    void Print() const {
        std::cout << name_ << ", age: " << age_
                  << ", score: " << score_ << '\n';
    }
private:
    std::string name_;
    int age_ = 0;
    int score_ = 0;
};

这段代码里有三个访问限定符需要先分清:

访问限定符类外能不能直接访问作用
public可以对外提供的接口
protected不可以主要留给派生类使用
private不可以类自己的实现细节

Student 的成员变量放在 private 里,外部代码不能直接写:

// stu.score_ = 999; // 编译不过

外部只能通过 SetScore 修改分数,而 SetScore 里面可以做范围控制。封装不是单纯把成员藏起来,而是把修改数据的入口收回来,让对象按设计好的规则工作。

还有一个容易忽略的细节:class 默认访问权限是 privatestruct 默认访问权限是 public。在 C++ 中,struct 也可以写成员函数,只是习惯上更常用 class 表达有封装规则的类型。

三、对象、成员函数和this指针

类本身只是一个类型,对象才是这个类型创建出来的具体变量。

int main() {
    Student s1;
    Student s2;
    s1.SetInfo("Li Hua", 18, 92);
    s2.SetInfo("Wang Ming", 19, 105);
    s1.Print();
    s2.Print();
}

s1s2 都是 Student 对象,它们各自保存一份成员变量。s1 的分数和 s2 的分数不会混在一起。

成员函数不需要每个对象都保存一份。对象里主要保存成员变量,成员函数是一份公共代码。那成员函数怎么知道自己现在操作的是 s1 还是 s2 呢?

关键就在隐藏的 this 指针。

比如我们写:

s1.SetScore(80);

可以粗略理解成:编译器帮我们把当前对象的地址传了进去。

SetScore(&s1, 80);

真实语法不是这样写,但这样理解比较顺。成员函数内部访问成员变量时,本质上是在通过 this 找到当前对象。

void SetScore(int score) {
    this->score_ = score;
}

大多数时候 this-> 可以省略。只有在形参名和成员变量名冲突,或者需要返回当前对象本身时,this 才会比较常见。

四、对象大小和内存对齐

我之前容易误以为:类里面有成员函数,对象大小应该也要把函数算进去。

实际并不是这样。在不考虑虚函数等机制时,对象大小主要和成员变量有关,成员函数不会放进每个对象里。

#include <iostream>
class Empty {};
class Example {
private:
    char c_;
    int i_;
};
int main() {
    std::cout << sizeof(Empty) << '\n';
    std::cout << sizeof(Example) << '\n';
}

空类对象的大小通常是 1。因为对象需要有唯一地址,如果大小是 0,多个对象的地址就不好区分。

Example 的大小也不一定是 1 + 4 = 5,因为还要考虑内存对齐。可以先这样理解:编译器会为了让 CPU 更方便访问数据,在成员之间补一些空字节。

刚开始不用死记所有对齐计算细节,先记住两个结论就够用:

  • 对象大小主要看非静态成员变量,不是看成员函数。
  • 成员变量顺序会影响对象大小,因为有内存对齐。

五、默认成员函数是什么

学到类和对象中间部分时,会遇到“默认成员函数”这个说法。

它指的是:我们不写时,编译器可能自动生成的成员函数。基础阶段先重点看这几个:

默认成员函数作用
默认构造函数不传实参时初始化对象
析构函数清理对象管理的资源
拷贝构造函数用已有对象创建新对象
赋值运算符重载已存在对象之间赋值
取地址运算符重载对对象取地址
const 取地址运算符重载对 const 对象取地址

这里先按基础阶段常见的六个默认成员函数理解,不建议只背数量,更重要的是问一句:

编译器默认生成的行为,够不够这个类使用?

如果类里只是普通成员变量,默认生成的函数很多时候够用。但如果类自己管理资源,比如申请堆空间、打开文件、管理锁,就要谨慎。默认拷贝可能只是浅拷贝,默认析构也不一定能完成资源释放。

后面几个小节,主要围绕构造、析构、拷贝构造和赋值重载展开。

六、构造函数:让对象一出生就是可用的

在没有构造函数时,我们可能会写一个 Init 函数:

Student s;
s.SetInfo("Li Hua", 18, 92);

问题在于,如果忘了调用 SetInfo,对象就可能处在一个没有正确初始化的状态。

构造函数正是用来处理这件事的:对象创建时自动调用,用来完成初始化。

class Student {
public:
    Student(const std::string& name, int age, int score)
        : name_(name), age_(age) {
        SetScore(score);
    }
    void SetScore(int score) {
        if (score < 0) {
            score_ = 0;
        } else if (score > 100) {
            score_ = 100;
        } else {
            score_ = score;
        }
    }
    void Print() const {
        std::cout << name_ << ", age: " << age_
                  << ", score: " << score_ << '\n';
    }
private:
    std::string name_;
    int age_;
    int score_;
};

使用时直接写成:

Student s("Li Hua", 18, 92);
s.Print();

构造函数的几个基本特点:

  • 函数名和类名相同。
  • 没有返回值,连 void 也不写。
  • 创建对象时自动调用。
  • 可以重载。

默认构造函数也需要单独分清。只要“不传实参就能调用”的构造函数,都可以叫默认构造函数。

class Point {
public:
    Point() : x_(0), y_(0) {}
    Point(int x, int y) : x_(x), y_(y) {}
private:
    int x_;
    int y_;
};

这里的 Point() 是默认构造函数。

如果写成全缺省参数,也属于不传参就能调用:

class Point {
public:
    Point(int x = 0, int y = 0) : x_(x), y_(y) {}
private:
    int x_;
    int y_;
};

注意不要同时写一个无参构造和一个全缺省构造,否则 Point p; 调用哪个函数会产生歧义。

七、析构函数:对象结束前清理资源

构造函数负责初始化,析构函数负责对象销毁前的清理。

如果一个类只是保存 intdoublestd::string 这类成员,通常不需要自己写析构函数。编译器生成的默认析构就够用了。

但如果类里自己申请了资源,比如手动 new[] 了一块空间,就必须考虑释放问题。

#include <algorithm>
#include <cstddef>
class IntBuffer {
public:
    explicit IntBuffer(std::size_t size)
        : data_(new int[size]{}), size_(size) {}
    ~IntBuffer() {
        delete[] data_;
    }
private:
    int* data_;
    std::size_t size_;
};

析构函数的特点:

  • 名字是 ~类名
  • 没有参数,也没有返回值。
  • 对象生命周期结束时自动调用。

这里最需要分清的是:析构函数不是用来销毁对象本身的内存,而是用来清理对象内部管理的资源。

局部对象在函数结束时,它所在的栈帧会被释放;析构函数真正负责的是把对象内部申请的堆空间、文件句柄、锁等资源处理干净。

八、拷贝构造:新对象用旧对象初始化

拷贝构造函数处理的是“用一个同类型对象初始化另一个新对象”。

Student s1("Li Hua", 18, 92);
Student s2(s1);
Student s3 = s1;

s2(s1)s3 = s1 都是在创建新对象,因此调用的是拷贝构造。

如果类里没有自己管理资源,编译器默认生成的拷贝构造通常够用。比如 Student 里面的 std::string 自己知道怎么拷贝。

IntBuffer 这种类不能直接依赖默认拷贝构造。

IntBuffer b1(10);
IntBuffer b2 = b1; // 如果用默认拷贝,会有问题

默认拷贝只会把指针值复制一份,也就是两个对象的 data_ 指向同一块堆空间。等两个对象析构时,就可能释放同一块空间两次。

这就是浅拷贝容易出问题的地方。

更合适的做法是自己写深拷贝:不仅复制指针变量,还要重新申请一块空间,把内容复制过去。

class IntBuffer {
public:
    explicit IntBuffer(std::size_t size)
        : data_(new int[size]{}), size_(size) {}
    IntBuffer(const IntBuffer& other)
        : data_(new int[other.size_]), size_(other.size_) {
        std::copy(other.data_, other.data_ + other.size_, data_);
    }
    ~IntBuffer() {
        delete[] data_;
    }
private:
    int* data_;
    std::size_t size_;
};

判断要不要写拷贝构造时,可以先看这一点:

如果这个类自己管理资源,就要检查默认拷贝是不是只拷贝了资源句柄。

如果只是普通成员变量,不一定需要自己写;如果有裸指针管理堆空间,就要认真考虑自己实现。

九、赋值运算符重载:已经存在的对象互相赋值

拷贝构造和赋值看起来很像,但场景不同。

IntBuffer b1(10);
IntBuffer b2 = b1; // 拷贝构造:创建 b2
IntBuffer b3(5);
b3 = b1;           // 赋值:b3 已经存在

赋值运算符重载一般可以这样写:

class IntBuffer {
public:
    explicit IntBuffer(std::size_t size)
        : data_(new int[size]{}), size_(size) {}
    IntBuffer(const IntBuffer& other)
        : data_(new int[other.size_]), size_(other.size_) {
        std::copy(other.data_, other.data_ + other.size_, data_);
    }
    IntBuffer& operator=(const IntBuffer& other) {
        if (this == &other) {
            return *this;
        }
        int* newData = new int[other.size_];
        std::copy(other.data_, other.data_ + other.size_, newData);
        delete[] data_;
        data_ = newData;
        size_ = other.size_;
        return *this;
    }
    ~IntBuffer() {
        delete[] data_;
    }
private:
    int* data_;
    std::size_t size_;
};

这段代码里有几个细节值得注意:

  • operator= 返回 IntBuffer&,是为了支持连续赋值,比如 a = b = c
  • 要判断自赋值,避免 a = a 时把自己的资源先释放掉。
  • 先申请新资源,再释放旧资源,能避免中途申请失败时把原对象破坏掉。

这也引出一个常见判断:如果一个类需要自己写析构函数释放资源,通常也要认真检查拷贝构造和赋值运算符。

这三个函数经常要放在一起考虑:

函数什么时候调用主要任务
析构函数对象生命周期结束清理资源
拷贝构造用旧对象创建新对象初始化新对象
赋值重载已存在对象之间赋值替换当前对象内容

十、运算符重载:让类对象按自然方式使用

运算符重载不是为了让语法看起来花哨,而是为了让类对象的使用方式更符合直觉。

比如二维点 Point,判断两个点是否相等,用 == 通常比写 IsEqual 更自然。

#include <iostream>
class Point {
public:
    Point(int x = 0, int y = 0)
        : x_(x), y_(y) {}
    bool operator==(const Point& other) const {
        return x_ == other.x_ && y_ == other.y_;
    }
    Point operator+(const Point& other) const {
        return Point(x_ + other.x_, y_ + other.y_);
    }
    void Print() const {
        std::cout << "(" << x_ << ", " << y_ << ")\n";
    }
private:
    int x_;
    int y_;
};

使用时:

Point p1(1, 2);
Point p2(3, 4);
Point p3 = p1 + p2;
if (p1 == p2) {
    std::cout << "same\n";
}
p3.Print();

运算符重载有几条边界:

  • 不能自己创造 C++ 没有的运算符,比如 operator@
  • 运算符的优先级和结合性不会因为重载而改变。
  • 至少有一个操作数要是类类型,不能重载两个内置类型的运算。
  • 不是所有运算符都适合重载,关键要看语义是否自然。

比如 Point + Point 比较自然,但给 Student 重载 + 就不一定有意义。

++ 有一个容易混的地方:前置 ++ 和后置 ++ 都叫 operator++,C++ 用一个 int 形参区分后置版本。

class Counter {
public:
    Counter(int value = 0) : value_(value) {}
    Counter& operator++() {
        ++value_;
        return *this;
    }
    Counter operator++(int) {
        Counter old(*this);
        ++value_;
        return old;
    }
private:
    int value_;
};

前置 ++ 返回自增后的自己,后置 ++ 返回自增前的临时副本。这个行为要和内置类型保持一致。

十一、流插入为什么常写成全局函数

如果希望这样输出对象:

std::cout << p1 << '\n';

一般会把 operator<< 写成全局函数,而不是成员函数。

#include <iostream>
class Point {
public:
    Point(int x = 0, int y = 0)
        : x_(x), y_(y) {}
    int X() const { return x_; }
    int Y() const { return y_; }
private:
    int x_;
    int y_;
};
std::ostream& operator<<(std::ostream& out, const Point& p) {
    out << "(" << p.X() << ", " << p.Y() << ")";
    return out;
}

原因在于 << 左边是 std::cout,右边才是 Point 对象。如果写成成员函数,左操作数默认会变成当前对象,也就不符合 std::cout << p 这种使用习惯。

这里用 X()Y() 这样的公开接口读取私有成员。另一种做法是把 operator<< 声明成友元函数,后面会讲。

十二、const 成员函数:承诺不修改对象

成员函数后面加 const,表示这个函数不会修改当前对象。

class Point {
public:
    Point(int x = 0, int y = 0)
        : x_(x), y_(y) {}
    int X() const {
        return x_;
    }
    int Y() const {
        return y_;
    }
private:
    int x_;
    int y_;
};

const 实际修饰的是隐藏的 this 指针。普通成员函数里,this 可以理解成:

Point* const this

const 成员函数里,this 可以理解成:

const Point* const this

也就是说,不能通过这个 this 修改成员变量。

这点在写只读接口时很重要。比如 Print()Size()Empty()X() 这类函数,只要不修改对象,就应该考虑加 const。这样 const 对象也能调用它们。

十三、初始化列表:不是语法装饰

刚学构造函数时,我容易把初始化列表看成“另一种赋值写法”:

Point(int x, int y)
    : x_(x), y_(y) {}

后面再看引用成员、const 成员和类类型成员时,才发现初始化列表不是装饰,它表示成员变量真正被初始化的位置。

有些成员必须在初始化列表中初始化,比如:

  • 引用成员变量。
  • const 成员变量。
  • 没有默认构造函数的类类型成员。

看一个例子:

#include <string>
class FileName {
public:
    explicit FileName(const std::string& name)
        : name_(name) {}
private:
    std::string name_;
};
class LogRecord {
public:
    LogRecord(int id, int& level, const std::string& file)
        : id_(id), level_(level), file_(file) {}
private:
    const int id_;
    int& level_;
    FileName file_;
};

id_constlevel_ 是引用,file_ 的类型 FileName 没有默认构造函数,所以它们都必须在初始化列表中处理。

还有一个细节:成员变量的初始化顺序不是看初始化列表里写的顺序,而是看它们在类中声明的顺序。

class Demo {
public:
    Demo(int x) : b_(x), a_(b_) {}
private:
    int a_;
    int b_;
};

这段代码看起来先初始化 b_,再初始化 a_,但实际上 a_ 声明在前,会先初始化。这样的代码很容易埋下隐患。

所以写初始化列表时,最好让顺序和成员声明顺序保持一致。

十四、explicit:挡住不想要的隐式转换

单参数构造函数可能触发隐式类型转换。

class Id {
public:
    Id(int value) : value_(value) {}
private:
    int value_;
};
void PrintId(Id id) {}
int main() {
    PrintId(10); // int 被隐式转换成 Id
}

有时这种转换很方便,但很多时候会让代码不够明确。加上 explicit 后,就必须显式构造对象。

class Id {
public:
    explicit Id(int value) : value_(value) {}
private:
    int value_;
};
void PrintId(Id id) {}
int main() {
    // PrintId(10);      // 不允许隐式转换
    PrintId(Id(10));     // 明确创建 Id 对象
}

可以把 explicit 理解成一个提醒:这个类型不是随便从 int 变过来的,调用者要明确表达自己的意图。

十五、static 成员:属于类,不属于某个对象

普通成员变量是每个对象一份,而 static 成员变量是所有对象共享一份。

比如可以用它统计当前创建了多少个对象:

#include <iostream>
class ObjectCounter {
public:
    ObjectCounter() {
        ++count_;
    }
    ObjectCounter(const ObjectCounter&) {
        ++count_;
    }
    ~ObjectCounter() {
        --count_;
    }
    static int Count() {
        return count_;
    }
private:
    static int count_;
};
int ObjectCounter::count_ = 0;
int main() {
    std::cout << ObjectCounter::Count() << '\n';
    ObjectCounter a;
    ObjectCounter b;
    std::cout << ObjectCounter::Count() << '\n';
}

静态成员有几个特点:

  • 静态成员变量不属于某个具体对象。
  • 静态成员变量一般需要在类外定义和初始化。
  • 静态成员函数没有 this 指针。
  • 静态成员函数只能直接访问静态成员,不能直接访问普通成员变量。

因此,static 成员适合表示“这个类整体共享的一份信息”,比如对象计数、全局配置、工厂函数等。

十六、友元:方便,但不要滥用

友元可以让一个外部函数或另一个类访问当前类的私有成员。

以前面 Point 的输出函数为例,如果不想写 X()Y() 这种访问接口,也可以把它声明成友元:

#include <iostream>
class Point {
public:
    Point(int x = 0, int y = 0)
        : x_(x), y_(y) {}
    friend std::ostream& operator<<(std::ostream& out, const Point& p);
private:
    int x_;
    int y_;
};
std::ostream& operator<<(std::ostream& out, const Point& p) {
    out << "(" << p.x_ << ", " << p.y_ << ")";
    return out;
}

友元函数不是成员函数,只是被允许访问这个类的私有和保护成员。

友元的好处是方便,代价是会削弱封装。如果到处都开友元,类的私有成员就不再真正私有,后面维护会比较难。

可以这样记:

友元是给特殊关系开的门,不是为了图省事随便开后门。

十七、内部类:把强相关类型收进类域

如果一个类型只服务于另一个类,可以考虑把它定义成内部类。

class LinkedList {
private:
    struct Node {
        int value;
        Node* next;
    };
public:
    LinkedList() : head_(nullptr) {}
private:
    Node* head_;
};

这里的 Node 只给 LinkedList 内部使用,放在 private 里可以减少对外暴露。

内部类本质上还是一个独立的类型,只是它被放到了另一个类的作用域里,并且受访问限定符控制。

内部类的意义主要是表达“这个类型和外部类关系很近,外部不需要直接关心它”。

十八、匿名对象和拷贝优化

匿名对象就是没有名字的临时对象。

#include <iostream>
class Task {
public:
    void Run() {
        std::cout << "run task\n";
    }
};
int main() {
    Task().Run();
}

Task() 创建了一个临时对象,用完这一行基本就结束生命周期。

匿名对象在一些临时调用里很方便,但不要为了省一个变量名,把代码写得难读。

再看拷贝优化。按语法理解,函数传参、返回对象时可能会发生构造、拷贝构造、赋值等步骤。但现代编译器会在不改变程序结果的情况下省略一些拷贝。

Point MakePoint() {
    return Point(1, 2);
}
int main() {
    Point p = MakePoint();
}

从学习角度看,需要知道这里可能涉及临时对象和拷贝;从实际运行看,编译器可能直接把返回值构造到 p 的位置上。

这里不必一开始就纠结“到底调用了几次拷贝构造”。不同编译器、不同标准、不同优化选项下结果可能不同。更重要的是先理解对象生命周期,再知道编译器可能帮我们省掉一些中间步骤。

十九、复习时怎么串起来

如果把类和对象这块内容压缩成一条主线,可以这样记:

类负责封装数据和规则,对象负责承载具体状态。
对象创建时构造,使用中可能拷贝和赋值,结束时析构。
如果类自己管理资源,就要特别小心拷贝构造、赋值重载和析构函数。

再细一点,可以按下面这张表复习:

知识点先记什么
类和对象类是类型,对象是实例
封装成员变量放私有,通过接口控制访问
this 指针成员函数靠它知道当前对象是谁
构造函数对象创建时自动初始化
析构函数对象结束前清理资源
拷贝构造新对象用旧对象初始化
赋值重载已存在对象之间赋值
运算符重载让类对象按自然语义使用运算符
初始化列表成员真正初始化的位置
static 成员属于类,所有对象共享
友元有控制地突破访问限制
内部类把强相关的辅助类型收进类域

小结

C++ 类和对象这部分知识点不少,但主线可以收束到两件事:

  • 怎么把数据和操作组织成一个可靠的类型。
  • 怎么管理对象从创建到销毁的整个生命周期。

如果只是会写 classpublicprivate,还只能算知道语法。更需要慢慢建立的是对象意识:这个对象什么时候初始化?谁负责释放资源?拷贝时是复制值,还是共享了同一块资源?接口有没有把内部状态保护好?

这些问题理顺后,再往后学继承、多态、模板和 STL,很多设计就不会显得那么突然。它们本质上仍然在围绕一个问题展开:如何定义一个好用、可靠、边界清楚的类型。

到此这篇关于C++ 类和对象基础详解:从封装理解对象生命周期的文章就介绍到这了,更多相关C++ 类和对象内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++中`&`符号用法全解析

    C++中`&`符号用法全解析

    在C++里,&有三种不同的含义,你代码里主要用,主要用于引用和取地址,理解&对于单例模式至关重要,避免复制对象、提升效率,本文逐行解析代码中所有&的含义、作用用和区别,帮助你完全掌握,立即查看
    2026-05-05
  • C++实现LeetCode(122.买股票的最佳时间之二)

    C++实现LeetCode(122.买股票的最佳时间之二)

    这篇文章主要介绍了C++实现LeetCode(122.买股票的最佳时间之二),本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-07-07
  • C语言 bind()函数案例详解

    C语言 bind()函数案例详解

    这篇文章主要介绍了C语言 bind()函数案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • vscode 配置 C/C++编译环境(完整教程)

    vscode 配置 C/C++编译环境(完整教程)

    这篇文章主要介绍了vscode 配置 C/C++编译环境(完整教程),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • 用32位int型变量表示单引号括起来的四个字符的深入探讨

    用32位int型变量表示单引号括起来的四个字符的深入探讨

    本篇文章是对用32位int型变量表示单引号括起来的四个字符进行了详细的分析介绍,需要的朋友参考下
    2013-05-05
  • C语言字符函数、内存函数功能及实现代码

    C语言字符函数、内存函数功能及实现代码

    这篇文章主要介绍了C语言字符函数、内存函数 功能及实现,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-02-02
  • C语言每日练习之统计文本单词数及高频词

    C语言每日练习之统计文本单词数及高频词

    本文文大家准备了个C语言练习题:统计单词数并找出频率最高的单词,文中的示例代码讲解详细,对我们学习C语言有一定帮助,感兴趣的可以了解一下
    2022-05-05
  • C++之预处理功能详解

    C++之预处理功能详解

    预处理器是 C++ 编译器提供的一个工具,允许程序员在编译之前对源代码文件做出修改,本文将给大家通过代码示例详细介绍C++的预处理功能,需要的朋友可以参考下
    2023-05-05
  • 详解C语言的mem系列函数

    详解C语言的mem系列函数

    这篇文章主要为大家详细介绍了C语言的mem系列函数,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • C++中默认无参构造函数的工作机制浅析

    C++中默认无参构造函数的工作机制浅析

    构造函数主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用;析构函数主要作用在于对象销毁前系统自动调用,执行一些清理工作
    2023-02-02

最新评论