Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >三、从C语言到C++(三)

三、从C语言到C++(三)

作者头像
用户11332765
发布于 2024-10-28 08:20:52
发布于 2024-10-28 08:20:52
10300
代码可运行
举报
文章被收录于专栏:编程编程
运行总次数:0
代码可运行
三、从C语言到C++(三)

变量的初始化

在C语言中,变量的初始化通常是在声明变量之后,通过一个赋值语句来完成的。然而,C++引入了更强大的初始化特性,这些特性使得变量在声明时就能被赋予初始值,从而提高了代码的可读性和安全性。

以下是从C语言到C++变量初始化的对比和说明:

C语言中的变量初始化

在C语言中,变量通常在声明后通过赋值语句进行初始化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int x; // 声明一个整型变量x
x = 10; // 初始化x为10

C++中的变量初始化

C++提供了多种初始化变量的方式,其中一些是C语言所没有的。

  1. 默认初始化
    • 当定义变量时没有指定初值时,会进行默认初始化。
    • 对于内置类型(如int、double等),如果变量定义在函数内部(即{}内),则拥有未定义的值;如果定义在全局或命名空间作用域中(即{}外),则会被初始化为0。
    • 对于类类型的变量,默认初始化会调用其默认构造函数(如果存在的话)。
  2. 拷贝初始化
    • 拷贝初始化是指采用等号(=)进行初始化的方式。
    • 编译器会把等号右侧的初始值拷贝到新创建的对象中去。
    • 例如:int a = 0;string str1 = "hello";
  3. 直接初始化
    • 直接初始化是指采用小括号的方式进行变量初始化(小括号里一定要有初始值)。
    • 直接初始化直接调用与实参匹配的构造函数。
    • 例如:int a(0);string str1("hello");
  4. 值初始化
    • 值初始化是指使用了初始化器(即使用了圆括号或花括号)但却没有提供初始值的情况。
    • 对于内置类型,值初始化通常意味着初始化为0。
    • 对于类类型,值初始化会调用其默认构造函数(如果存在的话)。
    • 例如:int a = int();vector<int> vec(10);(这里的vec被值初始化为包含10个0的vector)
  5. 列表初始化(C++11及以后)
    • 对于数组、聚合类型(如结构体和类)等,可以使用花括号进行列表初始化。
    • 例如:int arr[3] = {1, 2, 3};struct Point { int x, y; }; Point p = {1, 2};
  6. 构造函数初始化列表(针对类)
    • 在创建类的对象时,可以通过构造函数初始化列表来初始化成员变量。
    • 这种方式在构造函数的函数体执行任何代码之前就已经对成员变量进行了初始化。
    • 例如:class MyClass { int value; public: MyClass(int v) : value(v) {} }; MyClass obj(10);
  7. 统一初始化语法(C++11及以后)
    • 使用花括号{}进行初始化,这种方式可以应用于所有类型的变量。
    • 例如:int x{10};double y{3.14};

总结:C++提供了多种初始化方式,包括默认初始化、拷贝初始化、直接初始化、值初始化、列表初始化、构造函数初始化列表和统一初始化语法。这些方式在不同的场景下有不同的用途,选择适当的初始化方式可以提高代码的可读性和安全性。

注意事项

  • 在C++中,尽量使用初始化而不是赋值来设置变量的初始值,因为这可以避免一些潜在的问题,如未初始化变量的使用。
  • 使用列表初始化和统一初始化语法可以提高代码的可读性和安全性,特别是在处理复杂的数据类型时。
  • 对于类类型,尽量使用构造函数初始化列表来初始化成员变量,而不是在构造函数的函数体内进行赋值。这可以提高效率,并确保成员变量在构造函数体执行任何代码之前就已经被正确初始化。

if / switch 语句初始化

在C语言中,if 和 switch 语句本身并不直接支持初始化变量的功能。然而,你可以在if或switch语句之前初始化变量,并在条件判断或case标签中使用这些变量。

例如,在C语言中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>  
  
int main() {  
    int x = 5; // 初始化变量  
  
    if (x == 5) {  
        printf("x is 5\n");  
    }  
  
    switch (x) {  
        case 5:  
            printf("x is 5 in switch\n");  
            break;  
        default:  
            printf("x is not 5 in switch\n");  
            break;  
    }  
  
    return 0;  
}

然而,在C++中,你可以使用作用域内的初始化(也称为内联初始化),这通常与if或switch语句没有直接关系,但可以在这些语句的上下文中使用。例如:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>  
  
int main() {  
    if (int x = 5; x == 5) { // 注意:C++17开始支持这种初始化  
        std::cout << "x is 5\n";  
    }  
  
    
      
    switch (int y = 5; y) {  
        case 5:  
            std::cout << "y is 5 in switch\n";  
            break;  
        default:  
            std::cout << "y is not 5 in switch\n";  
            break;  
    }  
  
    return 0;  
}

需要注意的是,C++17之前的标准不允许在if语句的条件部分进行变量初始化。从C++17开始,你可以像上面的示例那样在if语句的条件部分进行初始化。

另外,还要注意的是,在if语句的条件部分初始化的变量只在if语句的作用域内有效。这意味着你不能在if语句的外部访问这个变量。同样,在switch语句的case标签中,你也不能直接初始化变量(但你可以在case标签的代码块中初始化变量)。

动态内存分配

在C和C++中,动态内存分配都是编程的重要部分,允许程序在运行时根据需要分配和释放内存。虽然两者在语法和特性上有所不同,但基本概念是相似的。

C语言中的动态内存分配

在C语言中,我们主要使用malloc(), calloc(), realloc(), 和 free() 函数来进行动态内存分配和释放。

  1. malloc():分配指定字节数的内存,并返回指向该内存的指针。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int *ptr = (int*)malloc(sizeof(int) * 10); // 分配10个整数的内存
  1. calloc():与malloc()类似,但会初始化分配的内存为零。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int *ptr = (int*)calloc(10, sizeof(int)); // 分配10个整数并初始化为零
  1. realloc():调整已分配内存块的大小。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
ptr = (int*)realloc(ptr, sizeof(int) * 20); // 将ptr指向的内存大小调整为20个整数
  1. free():释放之前分配的内存。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
free(ptr); // 释放ptr指向的内存

C++中的动态内存分配

在C++中,除了可以使用C语言的函数外(尽管不推荐在C++中使用它们),还引入了newdelete操作符来进行动态内存分配和释放。

  1. new:分配内存并调用对象的构造函数(如果有的话)。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
int *ptr = new int[10]; // 分配10个整数的内存

对于对象,可以使用new来分配内存并直接初始化对象:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
std::string *strPtr = new std::string("Hello, World!");
  1. delete:释放之前使用new分配的内存,并调用对象的析构函数(如果有的话)。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
delete[] ptr; // 释放ptr指向的内存(对于数组)

对于单个对象,使用不带[]delete

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
delete strPtr; // 释放strPtr指向的内存并调用std::string的析构函数

new/deletemalloc/free 的区别

new/deletemalloc/free 在C++中都是用于动态内存分配的,但它们之间存在一些重要的区别。以下是这些区别的主要点:

  1. 所属语言
    • mallocfree 是C语言中的函数,用于在堆上分配和释放内存。
    • newdelete 是C++中的运算符,用于在堆上分配和释放内存。
  2. 构造函数和析构函数
    • 使用 new 分配的对象会自动调用其构造函数(如果存在)。
    • 使用 delete 释放的对象会自动调用其析构函数(如果存在)。
    • mallocfree 则不会调用构造函数或析构函数。
  3. 类型安全
    • new 运算符返回的是对象的指针,具有类型信息,因此是类型安全的。
    • malloc 返回的是 void* 类型的指针,需要显式地进行类型转换,这可能导致类型不安全。
  4. 异常安全性
    • 如果 new 在分配内存时无法满足请求(如内存不足),它会抛出一个 bad_alloc 异常。这使得错误处理更加灵活。
    • malloc 在内存不足时返回 NULL,需要程序员显式地检查并处理这种情况。
  5. 内存对齐
    • new 运算符考虑了内存对齐的问题,确保对象按照其类型的要求进行对齐。
    • malloc 则不保证内存对齐,这可能导致某些硬件平台上的性能问题或错误。
  6. 操作符重载
    • 在C++中,newdelete 运算符可以被重载,以提供自定义的内存分配和释放策略。
    • mallocfree 不能被重载。
  7. 初始化
    • 使用 new 分配的对象可以使用初始化列表进行初始化。
    • malloc 分配的内存需要手动进行初始化(如果需要的话)。
  8. 内存泄漏
    • 无论是使用 new/delete 还是 malloc/free,如果忘记释放分配的内存,都可能导致内存泄漏。但是,由于C++提供了更强大的工具(如智能指针和RAII),使用 new/delete 时更容易管理内存泄漏问题。

在大多数情况下,建议在C++中使用 new/delete 而不是 malloc/free,因为 new/delete 提供了更好的类型安全性和异常安全性,并且与C++的面向对象特性更加契合。然而,在某些情况下(如与C库交互或需要更底层的内存管理时),可能仍然需要使用 malloc/free

注意事项

  • 在C++中,尽量使用newdelete,而不是C语言的内存分配函数,因为newdelete会自动调用构造函数和析构函数,有助于管理对象的生命周期。
  • 在使用malloc()calloc()realloc()时,需要显式地转换返回的void*指针为所需的类型。但在C++中,使用new时不需要这样做。
  • 使用new[]分配的内存必须使用delete[]来释放,而不能使用delete。同样,使用new分配的内存应该使用delete来释放,而不是delete[]

原因如下:

  • 构造函数和析构函数的调用:当使用new[]分配数组时,C++会为数组中的每个对象调用构造函数(如果需要)。同样地,当使用delete[]释放数组时,C++会为数组中的每个对象调用析构函数。如果你只使用delete而不是delete[]来释放数组,那么只有数组的第一个对象的析构函数会被调用,而其他对象的析构函数则不会被调用,这可能导致资源泄漏或其他未定义的行为。
  • 内存管理:new[]不仅分配了数组中每个对象所需的内存,还分配了额外的内存来存储数组的大小或其他信息(尽管这通常是编译器实现的细节)。这些信息对于delete[]来说是必要的,因为它需要知道要调用多少个析构函数以及要释放多少内存。如果你只使用delete,编译器将不知道这些信息,从而导致不正确的内存管理。
  • 类型安全:虽然这与必须使用delete[]的原因不直接相关,但值得注意的是,new[]和delete[]是专门为数组设计的,并且与C++的类型系统兼容。这有助于确保在释放内存时不会发生类型错误。
  • 避免内存泄漏和未定义行为:未正确释放内存(如使用delete而不是delete[])会导致内存泄漏和未定义行为。内存泄漏会浪费系统资源,而未定义行为可能导致程序崩溃或产生不可预测的结果。
  • 忘记释放已分配的内存会导致内存泄漏,这是一个常见的编程错误。使用智能指针(如std::unique_ptrstd::shared_ptr)可以帮助自动管理内存,减少内存泄漏的风险。

定位放置

在C++中,new运算符有一个重载版本,称为定位放置new(placement new)。定位放置new允许程序员在预先分配的内存区域上构造对象,而不是让new自动分配内存。这在某些高级场景(如内存池管理、自定义内存分配策略或对象复用)中非常有用。

定位放置new的语法如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void* place = malloc(sizeof(T)); // 或者其他预先分配的内存
T* ptr = new(place) T(args); // 在place指向的内存上构造T类型的对象
// ... 使用ptr指向的对象 ...
ptr->~T(); // 显式调用析构函数
free(place); // 如果使用malloc分配的内存,需要显式释放

注意几个关键点:

  1. malloc(或其他类似函数)用于预先分配内存。这不是必须的,但通常用于演示目的,因为new本身会分配内存。
  2. new(place)语法用于在指定的内存地址place上构造对象。T(args)是传递给对象构造函数的参数列表。
  3. 当你完成对象的使用后,必须显式调用对象的析构函数(在这个例子中是ptr->~T()),因为定位放置new不会自动调用析构函数或释放内存。
  4. 如果你使用malloc分配了内存,那么还需要使用free来释放它。但是,如果你是在栈上或其他已管理的内存区域中预先分配了内存,则不需要这一步。

下面是一个简单的示例,展示了如何使用定位放置new

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <iostream>
#include <cstdlib> // 为了使用malloc和free

class MyClass {
public:
    MyClass(int value) : value_(value) {
        std::cout << "Constructing MyClass with value " << value_ << std::endl;
    }
    ~MyClass() {
        std::cout << "Destructing MyClass with value " << value_ << std::endl;
    }
    void printValue() const {
        std::cout << "Value: " << value_ << std::endl;
    }
private:
    int value_;
};

int main() {
    char buffer[sizeof(MyClass)]; // 预先分配的内存
    MyClass* ptr = new(buffer) MyClass(42); // 使用定位放置new构造对象
    ptr->printValue(); // 输出:Value: 42
    ptr->~MyClass(); // 显式调用析构函数
    // 注意:不需要释放buffer,因为它是栈上的数组
    return 0;
}

在这个示例中,我们在栈上预先分配了一个足够大的字符数组buffer来存储MyClass类型的对象。然后,我们使用定位放置newbuffer上构造了一个MyClass对象,并调用了它的printValue方法。最后,我们显式调用了析构函数来清理对象,但不需要(也不应该)释放buffer,因为它是在栈上分配的。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-06-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
彻底摘明白 C++ 的动态内存分配原理
大家好,我是 V 哥。在C++中,动态内存分配允许程序在运行时请求和释放内存,这对于处理不确定大小的数据或者在程序执行过程中动态调整数据结构非常有用。C++主要通过new和delete运算符(用于对象)以及malloc、calloc、realloc和free函数(继承自C语言)来实现动态内存分配,下面详细介绍它们的原理。先赞再看后评论,腰缠万贯财进门。
威哥爱编程
2025/02/19
1940
【C/C++】——小白初步了解——内存管理
特点:如果新大小大于原大小,新分配的内存区域中的内容是不确定的;如果新大小小于原大小,超出的内容将被丢弃。
小李很执着
2024/06/15
1240
【C++内存管理】:new与delete,operator new与operator delete
📝前言: 上篇文章【C++高潮:类与对象】我们对C++的类与对象的知识点进行了讲解。 这篇文章我们在C语言内存管理的基础上探讨一下C++内存的管理: 1,C/C++内存分布 2,C语言内存管理 3,C++内存管理方式 4,operator new与operator delete 5,new和delete的实现原理 6,定位new表达式 7,malloc/free和new/delete的区别
用户11029137
2025/03/12
1340
C++内存管理深度总结(近万字详解!)
在C语言中,动态内存管理主要通过malloc、calloc、realloc和free这四个函数进行。以下是一个简化的代码示例,展示了如何在C语言中使用这些函数来动态分配、使用和释放内存:
suye
2024/10/16
2060
【C++指南】C++内存管理 深度解析
在 C++ 中,内存管理是程序开发中至关重要的一环。由于 C++ 允许程序员直接操作内存,这既赋予了极大的灵活性,也带来了一定的复杂性和风险。高效且正确的内存管理对于编写高性能、稳定可靠的 C++ 程序起着关键作用。
倔强的石头
2024/12/06
1410
【C++指南】C++内存管理 深度解析
[C++] 深度剖析C_C++内存管理机制
定位new表达式语法:void* operator new(size_t, void* place) noexcept { return place; }
DevKevin
2024/07/25
890
[C++] 深度剖析C_C++内存管理机制
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
在 C 和 C++ 中,内存可以分为多个区域,包括栈、堆、数据段、代码段等。这些区域分别用来存储不同类型的数据。通过以下示例代码,我们可以直观地理解这些区域的作用:
半截诗
2024/10/09
4100
【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
今天你学C++了吗?——内存管理
前面在C语言阶段的一篇博客 C语言——动态内存分配我们提到过C/C++程序中内存区域的划分~
用户11352420
2024/12/25
950
今天你学C++了吗?——内存管理
C/C++内存管理
C/C++作为广泛使用的系统级编程语言,提供了直接操控内存的能力,这也意味着开发者需要对内存管理有深刻的理解。本文旨在深入浅出地讲解C/C++内存管理机制,包括内存分布、动态内存分配与释放、以及内存管理的最佳实践。
DevKevin
2024/05/24
720
C/C++内存管理
【c++】动态内存管理
之前在C语言当中,我们学习了动态内存管理的相关知识以及使用malloc/calloc/realloc/free函数实现堆区中动态内存的分配:
ephemerals__
2024/10/24
1210
【c++】动态内存管理
【C/C++内存管理】——我与C++的不解之缘(六)
对于以上代码,这些创建的全局变量,局部变量以及静态变量等都分别存放在内存的哪些区域?
星辰与你
2024/10/17
970
【C/C++内存管理】——我与C++的不解之缘(六)
C++初阶:C/C++内存管理、new与delete详解
在C++中,new和delete是用于动态内存管理的运算符,它们提供了对malloc、calloc、realloc和free等C语言内存管理函数的更高级的封装和功能。
是Nero哦
2024/01/30
3270
C++初阶:C/C++内存管理、new与delete详解
C/C++内存管理
选项: A.栈 B.堆 C.数据段(静态区) D.代码段(常量区) globalVar在哪里?____ staticGlobalVar在哪里?____ staticVar在哪里?____ localVar在哪里?____ num1 在哪里?____ char2在哪里?____ *char2在哪里?____ pChar3在哪里?____ *pChar3在哪里?____ ptr1在哪里?____ *ptr1在哪里?____ 是不是有点乱,看一下图解吧。
小志biubiu
2025/02/27
930
C/C++内存管理
C++从入门到精通——C++动态内存管理
C++动态内存管理涉及使用new和delete操作符来动态分配和释放堆内存。new用于在堆上分配内存并初始化对象,delete用于释放先前分配的内存。此外,C++还提供了智能指针如std::unique_ptr和std::shared_ptr来自动管理内存,以避免内存泄漏和悬挂指针。这些智能指针在超出作用域时会自动删除其所指向的对象。
鲜于言悠
2024/04/22
2220
C++从入门到精通——C++动态内存管理
【C++】Chapter02 内存管理
在以往的C语言中,我们一般使用malloc、calloc、realloc来进行内存管理;在C++中,虽然也可以继续兼容使用,但是难免会觉得用起来过于复杂和繁琐,所以C++中一般使用以下的两个操作符进行动态内存管理。
Skrrapper
2025/03/25
550
【C++】Chapter02 内存管理
C/C++内存详解
malloc、realloc、calloc 和 free 是C语言中用于动态内存管理的标准库函数,它们定义在<stdlib.h>头文件中。这些函数允许程序在运行时根据需要分配和释放内存,而不是在编译时静态地分配内存。这对于处理未知大小的数据或需要动态增长的数据结构(如链表、树等)特别有用。
破晓的历程
2024/09/04
1120
C/C++内存详解
【C++】探索C++内存管理:机制揭秘与内存安全
需要注意的是,C标准库中的malloc函数的具体实现可能因编译器和操作系统的不同而有所差异,上述步骤仅为一种常见的实现方式。
大耳朵土土垚
2024/05/03
1590
【C++】探索C++内存管理:机制揭秘与内存安全
【C++】C/C++内存管理详解
在之前的C语言中就有提到动态内存管理 【C语言】动态内存管理,那么在C++中又是怎么样的呢?话不多说,正文开始。
zxctscl
2024/03/08
1220
【C++】C/C++内存管理详解
【C++】第六节—内存管理
现在我们必须要清楚,写一个程序,程序里面的变量都放在我们刚才介绍的那个区域呢?见下题目(面试题,面试会考这样的题,那可得好好学清楚了)
云边有个稻草人
2025/03/31
660
【C++】第六节—内存管理
C/C++内存管理
2. 内存映射段是高效的I/O映射方式,用于装载一个共享的动态内存库。用户可使用系统接口
绝活蛋炒饭
2024/12/16
930
C/C++内存管理
相关推荐
彻底摘明白 C++ 的动态内存分配原理
更多 >
LV.1
这个人很懒,什么都没有留下~
领券
社区富文本编辑器全新改版!诚邀体验~
全新交互,全新视觉,新增快捷键、悬浮工具栏、高亮块等功能并同时优化现有功能,全面提升创作效率和体验
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验