Boolan第二周笔记

本文包括第二周课程的主要内容(总结自Boolan C++ 微专业第二周课程),同时扩充了与课程内容相关的一些知识(总结自C++ Primer中文第五版高质量程序设计指南-- C++/C语言(第三版)(修订版)的相关内容),对一些易混淆的概念进行了区分。另外,我写了一些简单的代码,帮助自己理解概念。

1.第二周课程的主要内容

本周以string为例讲了如何实现一个带指针的类。因为合成的拷贝控制成员只是拷贝值(浅拷贝),含有指针的类就会只拷贝指针的值,而不是指针指向的对象。仅拷贝指针的值会造成两个指针指向同一个地方(二者互为别名),这样会使赋值双方中的右方指向的内容失去指针,造成内存泄露。而且,别名在编程中有隐含的危险,对其中一方的改变有可能影响另一方。尤其是当通过其中一方的指针删除二者共同指向的对象时,另一方指向的对象也不存在了,这往往不是我们想要看到的。因此class带指针就要自己写这两个函数,不可使用编译器的默认版本。上周的复数函数也有拷贝构造和拷贝赋值函数,这两个函数忠实地按位进行拷贝。当没有写的时候,编译器会构造默认版本

下图是string的默认构造函数和析构函数。

构造函数首先检查cstr的值是否是空指针,不是空指针就分配空间,长度是字符数目+1,1是结束符的空间;如果是空指针就分配一个字符的内存,放'\0',使之成为空字符串。

析构函数,当离开该类的作用域,调用析构函数,释放掉new出来的内存。有中括号的new和delete分别叫做array new,array delete,二者要互相配合,不要忘记写[],否则会造成内存泄漏(忘记写[]的话,程序只销毁了所有的指针和第一个指针指向的内容,其他指针指向的内容仍存在,但是没有指针指向它们了)。

下图是string的拷贝构造函数。

string的拷贝构造函数首先构造一块长度为str的长度加1的空间,然后拷贝内容,这就是深拷贝(拷贝指针指向的内容,而不是仅拷贝指针的值)。

下图是string的拷贝赋值函数。

 带指针的类的拷贝赋值函数需要注意的问题就是处理自我赋值。自我赋值的处理关乎效率和安全。一方面,如果检测到是自我赋值,则直接返回对象本身,这样不用执行下面的代码,提高了效率。另一方面,如果存在自我赋值而不进行检查,delete[] m_data就销毁该对象,以后的代码试图获得该对象的时候就会产生不确定行为。

2.与课程内容相关的一些知识

(1).指针

使用未经初始化的指针所引发的后果是无法预计的,因此最好初始化所有的指针,并且尽量等定义了对象后再定义指向它的指针

空指针(null pointer):使用nullptr初始化指针就会得到一个空指针。空指针不指向任何对象,在使用空指针之前代码可首先检查它是否为空

void* 指针:一种可以存放任意对象的特殊指针类型。这类指针的地址中存放的对象类型未知,而且无法访问。

空悬指针(dangling pointer):野指针(指向非法内存的指针,比如未被初始化的指针和已删除但是没有被置为nullptr的指针),被delete掉的指针。是一种无效的指针,但在很多机器上,这类指针仍然保存着已经释放了的动态内存的地址,它指向一块曾经保存数据对象但现在已经无效的内存。为避免错误地使用空悬指针,可以在释放与其关联的内存之后给其赋值nullptr。这样程序员就知道该指针是无效指针。如果需要再次使用,可以为其重新分配内存并初始化。

如果一个问题可以使用迭代器也可以使用指针,优先使用迭代器或智能指针。(联想到了数组和vector等顺序容器,如果一个问题可以使用vector等顺序容器也可以使用数组使用vector等顺序容器比使用内置数组更好。)使用普通指针往往容易导致各种难以理解和处理的错误,使用迭代器智能指针(shared_ptr)会更加安全shared_ptr使用引用计数来管理指向同一对象的所有指针,当指向该对象的最后一个shared_ptr被销毁时,shared_ptr类自动销毁该对象,不易出错。

(2).内存分配

有三种方式:

一是从静态存储区域分配。内存在程序编译时就已分配好,在程序的整个运行期间都存在,如全局变量,static变量(作用域是整个程序)等。

二是在堆栈(stack)上分配。在函数执行期间,函数内局部变量(包括形参)的存储单元都在堆栈上。堆栈中创建的对象叫做stack object,也叫local object,函数结束时存储单元自动释放(堆栈清退)。

三是从堆(heap)(自由存储空间)上分配,亦称动态内存分配(比如用new, malloc)。程序员在不需要这类对象的时候必须手动销毁它们(delete, free)。如果忘记删除这类对象,就会发生内存泄漏,因为指针指向的对象还在,但是指针没有了,也就没有其他途径可以访问到对象了。new将分配内存和构造对象的工作放在了一起,C++有一个allocator类,它可以分配一块未经构造的内存,再使用迭代器和allocator相关的算法构造对象,更加灵活。

由于动态存储易发生错误,如果使用堆栈存储和静态存储就可以满足应用要求,就不要使用动态存储。

(3).字符串

字符字面值:'a'

字符串字面值:"a"

字符数组:元素为字符变量的数组。

C风格字符串(C-style character string):以空字符('\0')结束(null terminated)的字符数组。不是一种类型,只是一种为表达和使用字符串而形成的一种约定俗成的写法。

一般用指针来操作这类字符串。C++默认char*表示字符串。由于使用数组时其实真正使用的是指向数组首元素的指针,因此使用C风格字符串就是在使用指向字符串头部的指针。处理C风格字符串的函数有strlen(p), strcmp(p1, p2), strcat(p1,p2), strcpy(p1,p2)等等,都是将C风格字符串的名字(也就是指向字符数组首元素的指针)传入函数,通过遍历指针来处理字符串中的元素。使用strcpy(p1,p2)时需要注意p1指向的空间要足够容纳p2的元素和一个空字符的数目,代码如下。

// P110 E3.40.cpp: 定义控制台应用程序的入口点。
// C++ Primer 中文第五版 第110页 习题3.40

#include "stdafx.h"
#include<iostream>
#include<cstring>
using namespace std;

int main()
{
    char c1[] = "hello";
    char c2[] = "world";
    int nSize = strlen(c1) + strlen(c2) + 1;
    char *pc = new char[nSize];
    strcpy_s(pc, nSize, c1);
    strcat_s(pc, nSize, c2);
    cout << pc << endl;

    delete pc;

    return 0;
}

 C++ 的string类对于字符串的操作与C风格字符串这种字符数组的操作相比要简单。以字符串的连接和拷贝为例,只需要使用+和=运算符,不必像strcpy(p1,p2)处理C风格字符串那样需要考虑内存管理(因为string类的实现里面已经对内存的使用做了处理,正如本周课程里面给的例子那样)。

#include "stdafx.h"
#include<iostream>
#include<string>
using namespace std;

int main()
{
    string s1 ("hello");
    string s2("world");
    string s3 = s1 + s2;
    cout << s3 << endl;

    return 0;
}

 

转载于:https://www.cnblogs.com/Dana-coder/p/8284573.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值