跳至主要內容

C++知识汇总(一)

CodeShouhu大约 31 分钟使用指南Markdown

C++知识汇总(一)

1,详细说明c++语言的特征以及每个特征的不同之处

C++ 是一种高效且通用的编程语言,它支持多种编程范式,包括过程式编程、面向对象编程和泛型编程。以下是 C++ 的一些主要特点:

  1. 高效性:C++ 提供了底层访问权限,允许程序员进行细微的内存管理和优化,从而实现了高效率。
  2. 面向对象:C++ 支持面向对象编程,包括封装、继承和多态等特性。
  3. 泛型编程:C++ 的模板功能支持泛型编程,这是一种编写可适应多种数据类型的代码的方式。
  4. 标准模板库 (STL):C++ 提供了一套强大的标准模板库,其中包含了多种通用的数据结构和算法。
  5. 可移植性:C++ 的设计目标之一就是能够在多种硬件和操作系统平台上运行。
  6. 混合范式:C++ 允许使用多种编程范式,包括过程式、面向对象和泛型编程。
  7. 强类型检查:C++ 是强类型语言,这意味着编译器会检查类型错误,从而提高代码安全性。
  8. 直接访问硬件:C++ 提供了直接访问硬件资源(如内存和磁盘)的能力。
  9. 支持异常处理:C++ 提供了异常处理机制,用于处理运行时错误。
  10. 广泛的用途:C++ 的用途非常广泛,包括开发操作系统、游戏、嵌入式系统、实时系统、高性能服务器和客户端应用等。

然而,C++ 的复杂性也是其一大特点。由于 C++ 提供了大量的功能和灵活性,学习和掌握这门语言可能需要较长的时间和深入的理解。此外,由于 C++ 允许直接访问内存和硬件资源,不谨慎的编程可能会导致内存泄漏、崩溃或其他问题。

#include <iostream>  
#include <string>  
  
// 定义一个Person类  
class Person {  
public:  
    Person(const std::string& name, int age) : name_(name), age_(age) {}  
  
    void SayHello() const {  
        std::cout << "Hello, my name is " << name_ << " and I am " << age_ << " years old." << std::endl;  
    }  
  
private:  
    std::string name_;  
    int age_;  
};  
  
int main() {  
    // 创建一个Person对象  
    Person person("Alice", 25);  
  
    // 调用对象的成员函数  
    person.SayHello();  
  
    return 0;  
}

这个程序定义了一个Person类,该类具有两个私有成员变量(name_age_)和一个公有成员函数(SayHello)。main函数中创建了一个Person对象,并调用了其SayHello函数,该函数输出了一个人的问候语。这个程序展示了C++的面向对象特点,以及如何使用C++创建一个简单的应用程序。

速记

高效性:允许程序员进行细微的内存管理和优化。面向对象泛型编程标准模板库 (STL)可移植性:能够在多种硬件和操作系统平台上运行;混合范式:允许使用多种编程范式;强类型检查:会检查类型错误;直接访问硬件支持异常处理

2,详细说明C语言和C++的区别?

C语言和C++是两种非常相似的编程语言,它们之间有很多共同点,但也存在一些明显的区别。以下是C语言和C++之间的一些主要区别:

  1. 面向对象 vs 面向过程:C语言是一种面向过程的编程语言,而C++是一种面向对象的编程语言。面向对象的编程允许程序员定义自己的类型(类),这些类可以有方法(函数)和数据成员(变量)。
  2. 命名空间:C++支持命名空间,而C语言不支持。命名空间允许程序员将代码组织成逻辑单元,并避免命名冲突。
  3. 标准模板库(STL):C++有一个强大的标准模板库(STL),其中包含了许多预先定义的数据结构和算法,而C语言没有这样的库。
  4. 异常处理:C++支持异常处理,而C语言不支持。异常处理允许程序员在运行时捕获和处理错误。
  5. 函数重载:C++支持函数重载,而C语言不支持。函数重载允许程序员定义多个具有相同名称但参数列表不同的函数。
  6. 类型检查:C++是强类型语言,而C语言是弱类型语言。这意味着C++在编译时会进行更严格的类型检查。
  7. 内存管理:虽然C和C++都允许程序员进行直接内存管理,但C++提供了更多的内存管理工具,如构造函数、析构函数和运算符new和delete。
  8. 输入/输出流:C++使用iostream库进行输入和输出,而C语言使用stdio.h库。
  9. 编译:C++通常比C语言需要更复杂的编译过程,因为C++有更多的特性需要处理。
#include <iostream>  
#include <string>  
  
// 定义一个Person类  
class Person {  
public:  
    Person(const std::string& name, int age) : name_(name), age_(age) {}  
  
    void SayHello() const {  
        std::cout << "Hello, my name is " << name_ << " and I am " << age_ << " years old." << std::endl;  
    }  
  
private:  
    std::string name_;  
    int age_;  
};  
  
int main() {  
    try {  
        // 创建一个Person对象  
        Person person("Alice", 25);  
  
        // 调用对象的成员函数  
        person.SayHello();  
    }  
    catch (const std::exception& e) {  
        std::cerr << "An error occurred: " << e.what() << std::endl;  
    }  
  
    return 0;  
}

这个程序定义了一个Person类,并展示了C++的面向对象编程特点。它使用了命名空间(std),以便使用标准库中的函数和类。同时,程序还使用了异常处理机制,如果在创建Person对象或调用其成员函数时出现错误,就会捕获并处理异常。这个程序与前面的C语言程序的输出类似,但实现方式有所不同。

尽管C++提供了许多比C语言更高级的特性,但C语言仍然在许多领域(如嵌入式系统和操作系统开发)中得到广泛应用,因为它更加简洁和高效。

速记

面向对象 vs 面向过程命名空间标准模板库(STL)异常处理函数重载类型检查内存管理C++提供了更多的内存管理工具,如构造函数、析构函数和运算符new和delete;输入/输出流:C++使用iostream库进行输入和输出,而C语言使用stdio.h库。编译:C++通常比C语言需要更复杂的编译过程,因为C++有更多的特性需要处理。

3,详细说明 C++中 struct 和 class 的不同之处

在 C++ 中,structclass 关键字都可以用来定义用户自定义的数据类型,它们之间有很多相似之处,但也存在一些区别。以下是 structclass 之间的一些主要区别:

  1. 默认的访问级别:在 struct 中,成员变量和成员函数的默认访问级别是 public,而在 class 中,默认访问级别是 private
  2. 继承方式:当 struct 被用作基类时,继承默认是 public 的,而当 class 被用作基类时,继承默认是 private 的。
  3. 用途:在 C++ 中,struct 通常用于定义简单的数据结构,如数组、链表等,而 class 则更多地用于定义复杂的对象,这些对象通常包含函数成员和私有数据成员。
  4. 兼容性:C++ 中的 struct 是与 C 语言兼容的,这意味着你可以在 C++ 中使用 C 语言中的 struct。然而,class 关键字在 C 语言中是不存在的,所以不能在 C 语言中使用 C++ 的 class
  5. 模板:在模板中,struct 可以省略模板参数名,而 class 则不能。例如,你可以写 template <typename T> struct X { T x; };,但不能写 template <typename T> class X { T x; };

总的来说,structclass 在 C++ 中的区别主要是默认访问级别和用途上的区别。其他的区别相对较小,而且在许多情况下,你可以根据个人的喜好和项目的需求来选择使用 struct 还是 class

#include <iostream>  
  
// 定义一个struct  
struct MyStruct {  
    int x; // 默认public,可以在外部访问  
};  
  
// 定义一个class  
class MyClass {  
    int x; // 默认private,只能在类内部访问  
public:  
    void setX(int value) { x = value; } // 提供公共接口来修改私有成员x  
    int getX() const { return x; } // 提供公共接口来获取私有成员x的值  
};  
  
int main() {  
    // 使用struct  
    MyStruct s;  
    s.x = 10; // 可以直接访问struct的成员x  
    std::cout << "MyStruct x: " << s.x << std::endl;  
  
    // 使用class  
    MyClass c;  
    c.setX(20); // 通过公共接口设置私有成员x的值  
    std::cout << "MyClass x: " << c.getX() << std::endl;  
    // 直接访问类的私有成员x会导致编译错误  
    // std::cout << "MyClass x: " << c.x << std::endl; // 错误!无法直接访问私有成员x  
  
    return 0;  
}
输出:
MyStruct x: 10  
MyClass x: 20

在这个例子中,MyStruct的成员x默认是public的,所以可以直接在外部访问。而MyClass的成员x默认是private的,所以无法在外部直接访问,必须通过类提供的公共接口来修改和获取它的值。

速记

struct 中,成员变量和成员函数的默认访问级别是 publicclassprivate。当 struct 被用作基类时,继承默认是 public 的,而当 classprivate 的。在 C++ 中,struct 通常用于定义简单的数据结构,而 class 则更多地用于定义复杂的对象;C++ 中的 struct 是与 C 语言兼容的,然而,class 关键字在 C 语言中是不存在的,在模板中,struct 可以省略模板参数名,而 class 则不能。例如,你可以写 template <typename T> struct X { T x; };,但不能写 template <typename T> class X { T x; };

4,在include头文件的查找路径的区别以及双引号""和尖括号<>的使用不同之处?

  1. 区别:

    (1)尖括号<>的头文件是系统文件,双引号""的头文件是自定义文件

    (2)编译器预处理阶段查找头文件的路径不一样。

  2. 查找路径:

    (1)使用尖括号<>的头文件的查找路径:编译器设置的头文件路径-->系统变量。

    (2)使用双引号""的头文件的查找路径:当前头文件目录-->编译器设置的头文件路径-->系统变量。

5,详细说明C++结构体和C结构体的不同之处

C++ 结构体(struct)和 C 结构体(struct)在语法和用途上非常相似,但在 C++ 中,由于面向对象的特性,结构体有了更多的功能和用法。以下是 C++ 结构体和 C 结构体之间的一些主要区别:

  1. 成员函数的定义:在 C 语言中,结构体只能包含数据成员,不能包含函数成员(即方法)。而在 C++ 中,结构体可以包含函数成员,这使得结构体可以具有更复杂的行为。
  2. 访问修饰符:C++ 结构体支持访问修饰符(publicprivateprotected),而 C 语言中的结构体不支持。在 C++ 中,可以使用访问修饰符来控制结构体成员的访问权限。
  3. 继承:C++ 结构体可以继承自其他结构体或类,从而实现代码的重用和多态。而 C 语言中的结构体不支持继承。
  4. 模板:C++ 结构体可以使用模板,这使得结构体可以处理多种数据类型。而 C 语言中的结构体不支持模板。
  5. 构造函数和析构函数:C++ 结构体可以有构造函数和析构函数,用于初始化和清理结构体的状态。而 C 语言中的结构体不支持构造函数和析构函数。
  6. 运算符重载:C++ 结构体可以重载运算符,这使得结构体的行为可以更像内置的数据类型。而 C 语言中的结构体不支持运算符重载。

总的来说,C++ 结构体相比 C 结构体具有更多的面向对象特性,包括函数成员、访问修饰符、继承、模板、构造函数、析构函数和运算符重载等。这些特性使得 C++ 结构体更加强大和灵活。

以下是一个简单的例子来说明C++结构体和C结构体的区别:

cpp
#include <iostream>  
  
// C++结构体  
struct Person {  
    int age;  
    std::string name;  
  
    void display() {  
        std::cout << "Name: " << name << ", Age: " << age << std::endl;  
    }  
};  
  
int main() {  
    // C++结构体实例化并调用成员函数  
    Person p;  
    p.age = 25;  
    p.name = "John";  
    p.display();  
  
    return 0;  
}
在上面的例子中,Person 是一个C++结构体,包含一个整型成员 age 和一个字符串成员 name。此外,它还有一个成员函数 display 用于输出成员的值。这是C结构体所不具备的特性。

在C语言中,结构体只能包含数据成员,不能包含函数成员。以下是一个C语言结构体的例子:

c
#include <stdio.h>  
  
// C结构体  
struct Person {  
    int age;  
    char name[50];  
};  
  
int main() {  
    // C结构体实例化并输出成员的值  
    struct Person p;  
    p.age = 25;  
    strcpy(p.name, "John");  
    printf("Name: %s, Age: %d\n", p.name, p.age);  
  
    return 0;  
}
在上面的例子中,Person 是一个C结构体,同样包含一个整型成员 age 和一个字符数组成员 name。然而,它不能包含函数成员,所以我们在 main 函数中直接输出成员的值。

速记

成员函数的定义:在 C++ 中,结构体可以包含函数成员;访问修饰符:C++ 结构体支持访问修饰符,而 C 语言中的结构体不支持;继承:C++ 结构体可以继承自其他结构体或类;模板:C++ 结构体可以使用模板;构造函数和析构函数:C++ 结构体可以有构造函数和析构函数,用于初始化和清理结构体的状态。

6,在c++中导入其他文件的C函数的关键字是什么,并且在C++编译时和C有什么不同?

在C++中,导入C函数的关键字是extern,表达形式为extern "C"extern "C"的主要作用是为了能够正确实现C++代码调用其他C语言代码。加上extern "C"后,会指示编译器这部分代码按C语言的进行编译,而不是C++的。

C++和C在编译时的主要不同在于函数重载的处理。C++支持函数重载,即允许存在多个同名但参数列表不同的函数。在编译C++函数时,编译器会将函数的参数类型也加到编译后的代码中,而不仅仅是函数名。而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

速记

extern "C"的主要作用是为了能够正确实现C++代码调用其他C语言代码。C++和C在编译时的主要不同在于函数重载的处理。C++支持函数重载,在编译C++函数时,编译器会将函数的参数类型也加到编译后的代码中。而C语言并不支持函数重载,因此编译C语言代码的函数时不会带上函数的参数类型,一般只包括函数名。

7,详细说明C++从代码到可执行二进制文件的流程与区别

  1. 预处理:首先,C++预处理器(cpp)会处理源代码文件。预处理器的主要工作是处理以#开头的预处理指令,如#include#define等。例如,#include <iostream>会被替换为iostream头文件的内容。
  2. 编译:然后,编译器(cc1cl等)将预处理后的代码转换为汇编语言。编译器在这一步会对代码进行语法和语义检查,确保代码没有语法错误和类型错误。同时,编译器还会进行一些优化,如常量折叠和死代码消除等。
  3. 汇编:接着,汇编器(as)将编译器生成的汇编代码转换为目标机器代码(目标文件)。目标文件是二进制格式,但还不能直接运行,因为它可能还包含了其他目标文件的链接引用。
  4. 链接:最后,链接器(ldlink等)将多个目标文件和所需的库文件合并为一个单独的可执行文件。链接器的主要工作是解析目标文件中的链接引用,找到正确的函数和变量地址,并将它们合并到最终的可执行文件中。

速记

预处理:首先,C++预处理器的主要工作是处理以#开头的预处理指令,如#include#define等。编译:然后,编译器(cc1cl等)将预处理后的代码转换为汇编语言。汇编:接着,汇编器(as)将编译器生成的汇编代码转换为目标机器代码(目标文件)。链接:最后,链接器(ldlink等)将多个目标文件和所需的库文件合并为一个单独的可执行文件。

8,详细解释链接中的静态链接和动态链接的不同之处与联系

静态链接和动态链接是链接过程中的两种不同方式,它们的区别和联系如下:

  1. 静态链接:在静态链接中,编译器会将程序所需要的所有库文件都直接打包到最终的可执行文件中。这意味着可执行文件包含了程序运行所需的所有代码和数据,无需依赖外部库文件。因此,静态链接的可执行文件通常较大,但具有更好的可移植性和独立性。
  2. 动态链接:在动态链接中,编译器只会将程序所需要的函数和库名打包到可执行文件中,而不是整个库文件。当程序运行时,操作系统会根据这些信息将所需的库文件加载到内存中。因此,动态链接的可执行文件通常较小,但需要依赖外部库文件。动态链接的库文件可以被多个程序共享,节省了磁盘空间和内存。
  3. 区别:静态链接和动态链接的主要区别在于链接的时间和方式。静态链接是在编译时进行的,将所有需要的库文件直接合并到可执行文件中。而动态链接是在运行时进行的,只将所需的函数和库名打包到可执行文件中,然后在运行时由操作系统加载所需的库文件。此外,静态链接的可执行文件较大,但独立性强,而动态链接的可执行文件较小,但需要依赖外部库文件。
  4. 联系:静态链接和动态链接都是将多个目标文件和库文件合并为一个可执行文件的过程。它们都需要解析目标文件中的链接引用,找到正确的函数和变量地址。不同的是,静态链接将这些地址直接合并到可执行文件中,而动态链接则将这些地址留给操作系统在运行时解析和加载。

总的来说,静态链接和动态链接各有优缺点,选择哪种方式取决于具体的应用场景和需求。如果需要更好的可移植性和独立性,可以选择静态链接;如果需要节省磁盘空间和内存,可以选择动态链接。

速记

静态链接:编译器会将程序所需要的所有库文件都直接打包到最终的可执行文件中,无需依赖外部库文件。动态链接:编译器只会将程序所需要的函数和库名打包到可执行文件中。当程序运行时,操作系统会根据这些信息将所需的库文件加载到内存中。静态链接是在编译时进行的,将所有需要的库文件直接合并到可执行文件中。而动态链接是在运行时进行的,只将所需的函数和库名打包到可执行文件中,然后在运行时由操作系统加载所需的库文件。联系:静态链接和动态链接都是将多个目标文件和库文件合并为一个可执行文件的过程。它们都需要解析目标文件中的链接引用,找到正确的函数和变量地址。

9,在c++运行过程中静态链接是如何实现的?

静态链接的实现步骤主要包括以下几步:

  1. 空间分配:链接器首先会确定各个目标文件和库文件在最终可执行文件中的位置和大小。这一步通常包括按序叠加和相似段合并两个步骤。按序叠加是将目标文件和库文件按照顺序依次放置在可执行文件中。相似段合并是将具有相同属性(如可读、可写、可执行)的段合并在一起,以节省空间。
  2. 符号解析:链接器会解析目标文件中的符号引用,找到对应的符号定义。符号可以是函数、变量或其他命名实体。如果符号定义在当前目标文件中找不到,链接器会在其他目标文件或库文件中查找。
  3. 重定位:链接器会将符号引用的地址修改为实际的地址。这一步通常涉及到修改指令中的操作数,以使其指向正确的函数或变量地址。
  4. 生成可执行文件:最后,链接器会将所有的目标文件和库文件合并为一个单独的可执行文件。这个文件包含了程序运行所需的所有代码和数据,无需依赖外部库文件。

以上是静态链接的基本步骤。实际的链接过程可能会因编译器和操作系统的不同而有所差异,但大体上可以分为这四个步骤。

速记

空间分配:链接器首先会确定各个目标文件和库文件在最终可执行文件中的位置和大小。符号解析:链接器会解析目标文件中的符号引用,找到对应的符号定义。重定位:链接器会将符号引用的地址修改为实际的地址。生成可执行文件:最后,链接器会将所有的目标文件和库文件合并为一个单独的可执行文件。

10,在c++运行过程中动态链接是如何实现的?

动态链接的实现步骤主要包括以下几步:

  1. 启动动态链接器:由于动态链接器本身就是一个共享对象,它需要解决自己的重定位问题。这个过程被称为自举。自举代码首先找到动态链接器本身的“.dynamic”段,然后通过其中的信息获得动态链接器本身的重定位表和符号表,从而得到动态链接器本身的重定位入口。完成这一步后,动态链接器才可以开始使用自己的全局变量和静态变量。
  2. 装载所有需要的共享对象:动态链接器将可执行文件和链接器本身的符号表合并到一个全局符号表中,然后寻找可执行文件所依赖的共享对象,并将它们的符号表也合并到全局符号表中。
  3. 重定位和初始化:动态链接器会根据全局符号表中的信息,将可执行文件中的符号引用修改为实际的地址。这一步通常涉及到修改指令中的操作数,以使其指向正确的函数或变量地址。同时,动态链接器还会对共享对象进行初始化,例如执行构造函数等。

以上是动态链接的基本步骤。实际的链接过程可能会因操作系统和动态链接器的不同而有所差异,但大体上可以分为这三个步骤。

速记

启动动态链接器:代码首先找到动态链接器本身的“.dynamic”段,然后通过其中的信息获得动态链接器本身的重定位表和符号表,从而得到动态链接器本身的重定位入口。装载所有需要的共享对象:动态链接器将可执行文件和链接器本身的符号表合并到一个全局符号表中,然后寻找可执行文件所依赖的共享对象。重定位和初始化:动态链接器会根据全局符号表中的信息,将可执行文件中的符号引用修改为实际的地址。同时,动态链接器还会对共享对象进行初始化,例如执行构造函数等。

11,c++代码中宏定义和函数相互之间有什么不同之处?

宏定义和函数在C/C++编程中都被广泛使用,二者之间区别有:

  1. 代码长度:宏定义在每次使用时都会被插入到程序中。而函数代码只出现于一个地方,每次使用这个函数时,都调用那个地方的同一份代码。
  2. 执行速度:宏定义相对较快,因为它只是简单的文本替换,在预处理阶段起作用,相当于直接插入了代码,运行时不存在函数调用,执行起来更快;宏定义没有返回值;宏定义参数没有类型,不进行类型检查;宏定义不要在最后加分号。没有函数调用的开销。而函数调用需要一定的时间,包括参数传递、函数调用、返回结果等步骤。函数参数具有类型,需要检查类型。函数调用具有返回值。函数调用在运行时需要跳转到具体调用函数。
  3. 操作符优先级:在宏定义中,参数的求值是在所有周围表达式的上下文环境里,除非它们加上括号,否则邻近操作符的优先级可能会产生不可预料的结果。而在函数中,参数只在函数调用时求值一次,它的结果值传递给函数,表达式的求值结果更容易预测。
  4. 参数求值:在宏定义中,参数每次使用时都会重新求值,这可能导致具有副作用的参数产生不可预料的结果。而在函数中,参数在函数被调用前只求值一次,在函数中多次使用参数并不会导致多次求值过程。
  5. 参数类型:宏定义与类型无关,而函数的参数是与类型有关的。
  6. 递归:宏定义不能定义递归,而函数则可以。

总的来说,宏定义和函数各有其优缺点。在选择使用宏定义还是函数时,需要根据具体的应用场景和需求进行权衡。

#include <iostream>  
  
// 宏定义  
#define SQUARE(x) ((x) * (x))  
  
// 函数  
int square(int x) {  
    return x * x;  
}  
  
int main() {  
    int a = 5;  
  
    // 使用宏定义计算平方  
    std::cout << "Macro SQUARE(" << a << ") = " << SQUARE(a) << std::endl;  
  
    // 使用函数计算平方  
    std::cout << "Function square(" << a << ") = " << square(a) << std::endl;  
  
    return 0;  
}
输出:

scss
复制代码
Macro SQUARE(5) = 25  
Function square(5) = 25
在这个例子中,我们定义了一个宏SQUARE和一个函数square,它们都用于计算一个数的平方。你可以看到,在使用宏定义时,我们需要注意括号的使用,以避免出现意外的结果。而函数则自动处理了这些问题。此外,当你使用编译器进行调试时,函数可以更方便地进行逐步调试,而宏定义则不便于调试。

速记

宏定义在每次使用时都会被插入到程序中。而函数代码只出现于一个地方。宏定义相对较快,因为它只是简单的文本替换,在编译的预处理阶段起作用,相当于直接插入了代码,运行时不存在函数调用,执行起来更快;宏定义没有返回值;宏定义参数没有类型,不进行类型检查;宏定义不要在最后加分号。没有函数调用的开销。而函数调用需要一定的时间,包括参数传递、函数调用、返回结果等步骤。函数参数具有类型,需要检查类型。函数调用具有返回值。函数调用在运行时需要跳转到具体调用函数。在宏定义中,参数的求值是在所有周围表达式的上下文环境里。而在函数中,参数只在函数调用时求值一次,它的结果值传递给函数,表达式的求值结果更容易预测。宏定义不能定义递归,而函数则可以。

12,c++中宏定义和typedef的不同之处?

typedef主要用于定义类型别名。在编译阶段进行处理,有类型检查。宏定义不受作用域的限制,只要在预处理阶段能被找到就可以。而typedef有作用域的限制,只在定义它的作用域内有效。typedef会检查数据类型。typedef是语句,要加分号标识结束。宏定义使用#define预处理指令,后面跟宏名和替换文本。而typedef使用typedef关键字,后面跟已有的类型和新类型的别名。

#include <iostream>  
  
// 宏定义  
#define INT_PTR int*  
  
// typedef  
typedef int* IntPtr;  
  
int main() {  
    int x = 10;  
  
    // 使用宏定义声明指针  
    INT_PTR p1 = &x;  
  
    // 使用typedef声明指针  
    IntPtr p2 = &x;  
  
    std::cout << "Value of p1: " << *p1 << std::endl; // 输出:Value of p1: 10  
    std::cout << "Value of p2: " << *p2 << std::endl; // 输出:Value of p2: 10  
  
    return 0;  
}

在这个例子中,我们定义了一个宏INT_PTR和一个typedefIntPtr,它们都用于声明整型指针。你可以看到,在使用时,它们的效果是相同的。但是,typedef更类型安全,并且更便于调试。在实际开发中,推荐使用typedef来定义类型别名,因为它提供了更好的类型检查和可读性。

13、define宏定义和const的不同之处

#define宏定义和const在C++中都可用于创建常量,但它们之间存在一些重要的差异。

  1. 预处理器 vs 编译器: #define宏是由预处理器处理的,而const是由编译器处理的。预处理器在编译开始之前运行,它只是简单地替换文本,而不理解C++的语法或语义。另一方面,编译器理解C++代码,它可以进行类型检查和其他复杂的操作。const常量在定义时必须初始化,之后无法更改 。
  2. 作用域: const有作用域,而#define宏没有。const变量的作用域规则与常规变量相同,它们只在声明它们的块或文件中可见。另一方面,#define宏一旦定义,就在定义它的整个编译单元(通常是整个文件)中可见。const形参可以接收const和非const类型的实参
  3. 调试: 在调试时,const变量可以像其他变量一样被命名和查看,但宏不能。这使得const变量在使用调试器时更容易处理。
  4. 类型安全: const是类型安全的,而#define宏不是。const变量具有明确的类型,编译器可以进行类型检查。另一方面,#define宏只是简单的文本替换,没有类型信息。
  5. 存储: const变量有存储位置(通常是在数据段),const在程序运行中只有一份备份,且可以执行常量折叠,能将复杂的的表达式计算出结果放入常量表,而#define宏没有。这意味着const变量可以使用指针或引用访问,但#define宏不能。
  6. 性能和可读性: #define宏在预处理时进行文本替换,这可能会导致代码膨胀和其他问题。另一方面,const变量在编译时进行优化,通常不会导致这些问题。此外,const变量通常比#define宏更具可读性,因为它们的行为更像常规变量。
  7. 类的情况:const成员变量:不能在类定义外部初始化,只能通过构造函数初始化列表进行初始化,;不同类对其const数据成员的值可以不同,所以不能在类中声明时初始化 const成员函数:const对象不可以调用非const成员函数;非const对象都可以调用
#include <iostream>  
  
#define PI 3.14159  
const double pi = 3.14159;  
  
int main() {  
    std::cout << "PI: " << PI << std::endl;  // 输出:PI: 3.14159  
    std::cout << "pi: " << pi << std::endl;  // 输出:pi: 3.14159  
  
    // 下面这行代码会导致编译错误,因为PI是一个宏,没有类型,不能进行类型转换  
    // double radius = PI / 2;  
  
    // 下面这行代码是正确的,因为pi是一个const变量,有明确的类型,可以进行类型转换  
    double radius = pi / 2;  
    std::cout << "radius: " << radius << std::endl;  // 输出:radius: 1.57079  
  
    return 0;  
}

速记

#define宏是由预处理器处理的,而const是由编译器处理的。预处理器在编译开始之前运行,const常量在定义时必须初始化,之后无法更改 。const有作用域,而#define宏没有,const形参可以接收const和非const类型的实参, 在调试时,const变量可以像其他变量一样被命名和查看,但宏不能,const是类型安全的,而#define宏不是。const变量可以使用指针或引用访问,但#define宏不能类的情况:const成员变量:不能在类定义外部初始化,只能通过构造函数初始化列表进行初始化,;不同类对其const数据成员的值可以不同,所以不能在类中声明时初始化 const成员函数:const对象不可以调用非const成员函数;非const对象都可以调用

14,Static的特点与作用

不考虑类的情况:

  1. 静态全局变量和函数: 在全局作用域中,static可以用于声明变量和函数。静态全局变量只在其定义的文件中可见,其他文件无法访问它。静态全局变量在程序开始执行时就分配了内存,直到程序结束时才释放。在变量前面加上static关键字。初始化的静态变量会在数据段分配内存,未初始化的静态变量会在BSS段分配内存。直到程序结束,静态变量始终会维持前值。只不过全局静态变量和局部静态变量的作用域不一样;静态函数也只能在其定义的文件中调用,其他文件无法访问。在函数返回类型前加上static关键字,函数即被定义为静态函数。静态函数只能在本源文件中使用;

考虑类的情况:

  1. 静态成员变量: 在类中,static关键字可以用于声明静态成员变量。这些变量属于类本身,而不是类的任何特定对象。所有对象共享同一个静态成员变量,它可以在类的任何对象之外被访问和使用。静态成员变量在程序开始执行时就分配了内存,直到程序结束时才释放。
#include <iostream>  
  
class MyClass {  
public:  
    static int count;  
  
    MyClass() {  
        count++;  
        std::cout << "Object created, count = " << count << std::endl;  
    }  
};  
  
int MyClass::count = 0;  
  
int main() {  
    MyClass obj1; // 输出:Object created, count = 1  
    MyClass obj2; // 输出:Object created, count = 2  
    MyClass obj3; // 输出:Object created, count = 3  
  
    return 0;  
}

2.静态成员函数: static关键字也可以用于声明静态成员函数。这些函数可以在没有类的对象的情况下被调用,它们只能访问静态成员变量或其他静态成员函数。静态成员函数常常用于修改静态成员变量或执行与类对象无关的操作。

#include <iostream>  
  
class MyClass {  
public:  
    static int count;  
    static void printCount() {  
        std::cout << "Count = " << count << std::endl;  
    }  
};  
  
int MyClass::count = 0;  
  
int main() {  
    MyClass::printCount(); // 输出:Count = 0  
    MyClass obj;  
    MyClass::printCount(); // 输出:Count = 0(因为静态成员变量count没有被增加)  
  
    return 0;  
}

总结:

在不考虑类的情况下,static关键字主要用于限制变量和函数的可见性和生命周期。在考虑类的情况下,static关键字主要用于创建属于类本身的变量和函数,这些变量和函数可以在没有类的对象的情况下被访问和使用。