C++之指针_3

本文详细介绍了C++中的动态存储,包括使用new运算符创建动态结构,动态结构的成员访问,以及动态存储、静态存储和自动存储的区别。通过示例展示了如何使用new和delete管理内存,强调了内存泄漏的问题及其影响。此外,还讨论了结构和指针的组合使用,以及成员运算符的正确应用。
摘要由CSDN通过智能技术生成

使用new创建动态结构

在运行时创建数组优于在编译时创建数组,对于结构也是如此。需要在程序运行时为结构分配所需的空间,这也可以使用new运算符来完成。通过使用new,可以创建动态结构。同样,“动态”意味着内存是在运行时,而不是编译时分配的。由于类与结构非常相似,因此这里介绍的有关结构的技术也适用于类。

将new用于结构由两步组成:创建结构和访问其成员。要创建结构,需要同时使用结构类型和new例如,要创建一个未命名的 inflatable类型,并将其地址赋给一个指针,可以这样做:

inflatable * ps = new inflatable;

这将把存储inflatable结构的一块可用内存的地址赋给ps。创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只是知道它的地址。C++专门为这种情况提供了一个运算符:箭头成员运算符(->)。该运算符由连字符和大于号组成,可用于指向结构的指针,就像点运算符可用于结构名一样。例如,如果ps指向一个inflatable结构,则ps->price是被指向的结构的price成员。

提示:有时,C++新手在指定结构成员时,搞不清楚何时应使用句点运算符,何时应使用箭头运算符规则非常简单。如果结构标识符是结构名,如grubnose,则使用句点运算符;如果标识符是指向结构的指针,如pt,则使用箭头运算符

另一种访问结构成员的方法是,如果ps是指向结构的指针,则*ps就是被指向的值—结构本身。由于*ps是一个结构,因此(*ps).price是该结构的price成员。C++的运算符优先规则要求使用括号。示例程序使用new创建了一个未命名的结构,并演示了两种访问结构成员的指针表示法。

//程序4.21 newstrct.cpp  使用new创建结构体
#include <iostream>
struct inflatable
{
    char name[20];
    float volume;
    double price;
};

int main()
{
    using namespace std;
    inflatable * ps = new inflatable;   //为inflatable结构分配内存
    cout << "Enter name of inflatable item: ";
    cin.get(ps->name,20);   //结构成员的方法一
    cout << "Enter volume in cubic feet: ";
    cin >> (*ps).volume;
    cout << "Enter price: $";
    cin >> ps->name;
    cout << "Name: " << (*ps).name << endl;
    cout << "Volume" << ps->volume << "cubic feet\n";
    cout << "Price: $" << ps->price << endl;
    delete ps;
    return 0;
}

程序输出
Enter name of inflatable item: Fabulous Frodo
Enter volume in cubic feet: 1.4
Enter price: 27.99
Name: Fabulous Frodo
Volume: 1.4 cubic feet
Price: 27.99

一个使用new和delete的示例

下面介绍一个使用new和 delete来存储通过键盘输入的字符串的示例,下面程序定义了一个函数getname(),该函数返回一个指向输入字符串的指针,该函数将输入读入到一个大型的临时数组中,然后使用new[]创建一个刚好能够存储该输入字符串的内存块,并返回一个指向该内存块的指针。对于读取大量字符串的程序,这种方法可以节省大量内存(实际编写程序时,使用 string类将更容易,因为这样可以使用内置的new和 delete)

假设程序要读取100个字符串,其中最大的字符串包含79个字符,而大多数字符串都短得多。如果用char数组来存储这些字符串,则需要1000个数组,其中每个数组的长度为80个字符。这总共需要80000个字节,而其中的很多内存没有被使用。另一种方法是,创建一个数组,它包含1000个指向char的指针,然后使用new根据每个字符串的需要分配相应数量的内存,这将节省几万个字节。

//程序清单4.22 delete.cpp
//delete.cpp     使用删除操作
#include <iostream>
#include <cstring>  //或者string.h
using namespace std;
char * getname(void);  //函数声名
int main()
{
    char * name;       //创建一个未初始化的指针
    name = getname();   //将字符串的地址赋给name
    cout << name << " at " << (int*)name << "\n";
    delete [] name;    //释放内存

    name = getname();  //重新使用释放的内存
    cout << name << " at " << (int*) name << "\n";
    delete [] name;    //内存再次释放
    return 0;
}

char * getname()      //返回新字符串的地址
{
    char temp[80];    //临时存储
    cout << "Enter last name: ";
    cin >> temp;
    char * pn = new char [strlen(temp)+1];
    strcpy(pn,temp);  //赋值字符串到较小的空间

    return pn;       //当函数结束时temp被丢失
}

程序输出
Enter last name: Fredeldumpkin
Fredeldumpkin at 0x12e1358
Enter last name: Pook
Pook at 0x12e1358

函数getname()它使用cin将输入的单词放到temp数组中,然后使用new分配新内存,以存储该单词。程序需要strlen(temp)+1个字符(包括空字符)来存储该字符串,因此将这个值提供给new。获得空间后,getname()使用标准库函数 strcpy(将temp中的字符串复制到新的内存块中。该函数并不检查内存块是否能够容纳字符串,但 getname()通过使用new请求合适的字节数来完成了这样的工作。最后,函数返回pn,这是字符串副本的地址。

在main()中,返回值(地址)被赋给指针name.该指针是在main()中定义的,但它指向 getname()函数中分配的内存块。然后,程序打印该字符串及其地址。

在这个例子中, getname()分配内存,而main()释放内存,将new和 delete放在不同的函数中通常井不是个好办法,因为这样很容易忘记使用 delete.不过这个例子确实把new和 delete分开放置了,只是为了说明这样做也是可以的。

自动存储、静态存储和动态存储

根据用于分配内存的方法,C+有3种管理数据内存的方式:自动存储、静态存储和动态存储(有时也叫作自由存储空间或堆)。在存在时间的长短方面,以这3种方式分配的数据对象各不相同。下面简要地介绍每种类型(C++11新增了第四种类型—线性存储)

自动存储

在函数内部定义的常规变量使用自动存储空间,被称为自动变量,这意味着它们在所属函数被调用时自动产生,在该函数结束时消亡。如上面4.22程序中temp数组仅当getname()函数活动时存在,当程序控制权回到main()时,temp使用的内存被自动释放。如果getname()返回temp的地址,则main()中的name指针指向的内存将很快得到重新使用,这就是在getname()中使用new的原因。

实际上,自动变量是一个局部变量,其作用域为包含它的代码块。代码块是被包含在花括号中的一段代码。如果在其中的某个代码块定义了一个变量,则该变量仅在程序执行该代码块中的代码时存在。

自动变量通常存储在栈中。这意味着执行代码块时,其中的变量将依次加入到栈中,而在离开代码块时,将按相反的顺序释放这些变量,这被称为后进先出(LIFO)。因此,在程序执行过程中,栈将不断地增大和缩小。

静态存储

静态存储是整个程序执行期间都存在的存储方式。使变量成为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字 static:

static double fee = 56.60;
动态存储

new和 delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间( free store)或堆(heap)。该内存池中用于静态变量和自动变量的内存是分开的。程序4.22表明,new和 delete让我们能够在一个函数中分配内存,而在另一个函数中释放它。因此,数据的生命周期不完全受程序或函数的生存时间控制。与使用常规变量相比,使用new和 delete让程序员对程序如何使用内存有更大的控制权。然而,内存管理也更复杂了。在栈中,自动添加和删除机制使得占用的内存总是连续的,但new和delete的相互影响可能导致占用的自由存储区不连续,这使得跟踪新分配内存的位置更困难

栈、堆和内存泄漏

如果使用new运算符在自由存储空间(或堆)上创建变量后,没有调用delete,将发生什么情况呢?
如果没有调用delete,则即使包含指针的内存由于作用域规则和对象生命周期的原因而被释放,在自由存储空间上动态分配的变量或结构也将继续存在。实际上,将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效。这将导致内存泄漏。被泄漏的内存将在程序的整个生命周期内都不可使用;这些内存被分配出去,但无法收回。极端情况(不过不常见)是,内存泄漏可能会非常严重,以致于应用程序可用的内存被耗尽,出现内存耗尽错误,导致程序崩溃。另外,这种泄漏还会给一些操作系统或在相同的内存空间中运行的应用程序带来负面影响,导致它们崩溃。

类型组合

我们学习了数组,结构和指针,其实还可以用各种方式组合他们。先从结构开始。

struct antarctica_year_end
{
    int year;
    int birthday;
    /*还有一些实际有趣的数据等*/
};
  • 可以创建这种类型的变量
antarctica_year_end s01, s02, s03; //s01,s02,s03 是结构体

然后使用成员运算符访问其成员:

s01.year = 1998;
  • 可创建这种结构的指针:
antarctica_year_end * pa = &s02;

将该指针设置为有效地址后,就可使用间接成员运算符来访问成员:

pa->year = 1999;
  • 可创建结构数组:
antarctica_year_end trio[3];  //包含3个结构体的数组

然后可以使用成员运算符访问元素的成员:

trio[0].year = 2003;    //trio[0]是一个结构体

其中tiro是一个数组,tiro[0]是一个结构,而 trio[0].year是该结构的一个成员,由于数组名是一个指针(数组名是第一个元素的地址),因此也可使用间接成员运算符:

(trio+1)->year = 2004;  //等价于trio[1].year = 2004
  • 可创建指针数组:
antarctica_year_end *arp[3] = {&s01, $s02, &s03};

既然arp是一个指针数组,arp[1]就是一个指针,可将间接成员运算符应用于它,以访问成员:

std::cout << arp[1]->year << std::endl;
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
unique_ptr是C++11引入的智能指针类,定义在<memory>头文件中。它可以通过指定对象类型作为模板参数来创建。例如,unique_ptr<int>是指向int类型对象的指针,unique_ptr<double>是指向double类型对象的指针。\[1\] unique_ptr具有独占对象所有权的特点,即同一时间只能有一个unique_ptr指向一个对象,不能进行复制操作,只能进行移动操作。这意味着两个unique_ptr不能指向同一个对象。\[2\] unique_ptr还可以使用自定义的删除器(deleter),通过lambda表达式或函数指针来指定删除对象的方式。例如,在创建unique_ptr时可以指定一个lambda表达式作为删除器,用于在unique_ptr销毁时释放对象。\[3\] 总结来说,unique_ptr是C++中一种独占所有权的智能指针,它提供了自动释放对象的功能,并且可以通过指定对象类型和自定义删除器来灵活地管理资源。 #### 引用[.reference_title] - *1* [C++ 智能指针unique_ptr](https://blog.csdn.net/weixin_42322256/article/details/124058138)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [智能指针 unique_ptr 详解](https://blog.csdn.net/fuhanghang/article/details/113928128)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值