2020 我的C++的学习之路 第九章 内存模型与名称空间

以C++ Primer Plus为参考书籍,自身归纳知识点,加深记忆。

存储持续性

①自动存储持续性:在函数定义中声明的变量的存储持续性为自动,在程序开始执行所属的函数或代码块 时被创建,在执行完函数或代码块时,它们的内存被释放。

②静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储性为静态,在程序运行的整个运行过程都存在

③动态存储持续性:用new运算符分配的内存一直都在,直到delete或者程序结束为止,这种存储持续性为动态,有时被称为自由存储或者堆

④线程存储持续性(C++11):多核处理器中,让程序能狗将计算放在可并行处理的不同线程中,如果变量是使用关键字 thread_local声明的,则生命周期与所属的线程一样长

作用域与链接

作用域(scope)描述了名称在文件的多大范围内可见
链接性(linkage)描述了名称如何在不同单元间共享

自动存储持续性

①在默认情况下,函数中声明的函数参数和变量的存储持续性都为自动,局部作用域,无链接性。

int main()
{
	int n = 5;
	{
		int n =10;
		cout<<n;//n=10
	}
	cout<<n;//n=10
}

两个n都是局部变量,各自在局部作用域中作用,内部的n=10会隐藏外部n=5的定义,当程序离开内部代码块时,原来的定义又重新可见。

②自动变量被存储在栈中,后进先出,即最后加入到栈中的变量首先被弹出。

③atuo关键字在C语言中用于显式地指出变量为自动存储,但在C++11标准下,atuo改为自动类型推断

④register关键字在C语言中用于寄存器存储自动变量以提高访问速度,但在C++11标准下,register用于显式指出变量为自动存储

静态持续变量

静态存储持续性变量有三种链接性:外部链接(可跨文件访问),内部链接(当前文件访问),无链接(当前函数或代码块访问),这三种链接性在程序执行期间存在,编译器将分配固定的内存存储所有的静态变量,而不是栈。倘若没有显式的初始化静态变量,则编译器默认为0。

//a.cpp
int global = 100;//静态全局变量,外部链接的静态变量
static int n = 90;//内部链接的静态变量
int main()
{
	...
}

void fun1()
{
	static int cnt = 5;//无链接的静态变量
	int sa = 0;//自动变量
}

外部链接性静态持续变量:必须在代码块外面声明;
内部链接性静态持续变量:必须在代码块外面声明,并用static限定
无链接性静态持续变量:必须在代码块内部声明,并用static限定

静态持续性、外部链接性

倘若存在另外一个文件b.cpp使用a.cpp中的global变量,那么需要在b.cpp中使用关键字extern且不对global进行定义。

// external.cpp -- external variable
// compile with support.cpp
#include <iostream>
// external variable
double warming = 0.3;       // warming defined

// function prototypes
void update(double dt);
void local();

int main()                  // uses global variable
{
    using namespace std;
    cout << "Global warming is " << warming << " degrees.\n";//0.3
    update(0.1);            // call function to change warming//0.4
    cout << "Global warming is " << warming << " degrees.\n";//0.4
    local();                // call function with local warming//0.8 0.4
    cout << "Global warming is " << warming << " degrees.\n";//0.4
    // cin.get();
    return 0;
}
// support.cpp -- use external variable
// compile with external.cpp
#include <iostream>
extern double warming;  // use warming from another file

// function prototypes
void update(double dt);
void local();

using std::cout;
void update(double dt)      // modifies global variable
{
    extern double warming;  // optional redeclaration,此处可以省略声明,因为上部声明过一次
    warming += dt;          // uses global warming
    cout << "Updating global warming to " << warming;
    cout << " degrees.\n";
}

void local()                // uses local variable
{
    double warming = 0.8;   // new variable hides external one

    cout << "Local warming = " << warming << " degrees.\n";
        // Access global variable with the
        // scope resolution operator
    cout << "But global warming = " << ::warming;//::作用域解析运算符,放在变量名前面会使用全局版本,故此处应是0.4
    cout << " degrees.\n";
}

静态持续性、内部链接性

// twofile1.cpp -- variables with external and internal linkage
#include <iostream>     // to be compiled with two file2.cpp
int tom = 3;            // external variable definition
int dick = 30;          // external variable definition
static int harry = 300; // static, internal linkage
// function prototype
void remote_access();

int main()
{
    using namespace std;
    cout << "main() reports the following addresses:\n";
    cout << &tom << " = &tom, " << &dick << " = &dick, ";
    cout << &harry << " = &harry\n";
    remote_access();
    // cin.get();
    return 0; 
}
// twofile2.cpp -- variables with internal and external linkage
#include <iostream>
extern int tom;         // tom defined elsewhere
static int dick = 10;   // overrides external dick//覆盖上部的dick变量,此处为静态内部链接变量
int harry = 200;        // external variable definition,
                        // no conflict with twofile1 harry

void remote_access()
{
    using namespace std;
		
    cout << "remote_access() reports the following addresses:\n";
    cout << &tom << " = &tom, " << &dick << " = &dick, ";
    cout << &harry << " = &harry\n";
}

静态持续性、无链接性

无链接性的静态变量用关键词static在代码块中声明,即使该代码块不处于活动状态时,该变量一直存在,并且只在启动一次时进行初始化,以后再调用函数时,该变量不会被初始化

// static.cpp -- using a static local variable
#include <iostream>
// constants
const int ArSize = 10;

// function prototype
void strcount(const char * str);

int main()
{
    using namespace std;
    char input[ArSize];
    char next;

    cout << "Enter a line:\n";
    cin.get(input, ArSize);
    while (cin)
    {
        cin.get(next);
        while (next != '\n')    // string didn't fit!
            cin.get(next);      // dispose of remainder
        strcount(input);
        cout << "Enter next line (empty line to quit):\n";
        cin.get(input, ArSize);
    }
    cout << "Bye\n";
// code to keep window open for MSVC++
/*
cin.clear();
    while (cin.get() != '\n')
        continue;
    cin.get();
*/
    return 0;
}

void strcount(const char * str)
{
    using namespace std;
    static int total = 0;        // static local variable
    int count = 0;               // automatic local variable

    cout << "\"" << str <<"\" contains ";
    while (*str++)               // go to end of string
        count++;
    total += count;
    cout << count << " characters\n";
    cout << total << " characters total\n";
}

count每次调用都会被初始化为0,而total仅在第一次调用才会被初始化为0,每次调用后都会被存储,从而达到计数累加的效果。

说明符和限定符

存储说明符

①auto(C++11不再是说明符)
②register
③static
④extern
⑤thread_local
⑥mutable(即使结构或类变量为const,但用mutable可以使其声明的成员被修改)
同一个声明中不能使用多个说明符,但thread_local除外,可与static或extern结合使用

cv-限定符

①const
②volatile
关键词volatile表明,即使程序代码没有对内存单元进行修改,其值也可能会发生变化。

在C++中,倘若对全局变量(静态持续性,外部链接)加以const限定,可将该变量的链接性改为内部链接性,也就是说在C++中,全局变量const限定相当于static存储说明。

定位new运算符

通常new负责在堆中找到一个满足要求的内存块,而定位new运算符则可以指定要使用的位置

  double *ptr = new double[5];//动态分配内存
struct chaff
{
	char drss[20];
	int slag;
}

char buffer[500];
chaff *pch;
pch = new (buffer)chaff;//将结构chaff放入指定的buffer内存当中,并分配合适的内存大小
// newplace.cpp -- using placement new
#include <iostream>
#include <new> // for placement new
const int BUF = 512;
const int N = 5;
char buffer[BUF];      // chunk of memory
int main()
{
    using namespace std;

    double *pd1, *pd2;
    int i;
    cout << "Calling new and placement new:\n";
    pd1 = new double[N];           // use heap
    pd2 = new (buffer) double[N];  // use buffer array
    for (i = 0; i < N; i++)
        pd2[i] = pd1[i] = 1000 + 20.0 * i;
    cout << "Memory addresses:\n" << "  heap: " << pd1
        << "  static: " <<  (void *) buffer  <<endl;
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++)
    {
        cout << pd1[i] << " at " << &pd1[i] << "; ";
        cout << pd2[i] << " at " << &pd2[i] << endl;
    }

    cout << "\nCalling new and placement new a second time:\n";
    double *pd3, *pd4;
    pd3= new double[N];            // find new address
    pd4 = new (buffer) double[N];  // overwrite old data
    for (i = 0; i < N; i++)
        pd4[i] = pd3[i] = 1000 + 40.0 * i;
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++)
    {
        cout << pd3[i] << " at " << &pd3[i] << "; ";//pd[3]的地址与pa[1]的地址不一样,重新分配了一块新的地址
        cout << pd4[i] << " at " << &pd4[i] << endl;//pd[4]的地址与pa[2]的地址一样
    }

    cout << "\nCalling new and placement new a third time:\n";
    delete [] pd1;
    pd1= new double[N];
    pd2 = new (buffer + N * sizeof(double)) double[N]; 
    for (i = 0; i < N; i++)
        pd2[i] = pd1[i] = 1000 + 60.0 * i;
    cout << "Memory contents:\n";
    for (i = 0; i < N; i++)
    {
        cout << pd1[i] << " at " << &pd1[i] << "; ";
        cout << pd2[i] << " at " << &pd2[i] << endl;
    }
    delete [] pd1;//buffer指定的是静态内存,而delete只能用于指向的堆内存,因此不能用delete
    delete [] pd3;
    // cin.get();
    return 0;
}

名称空间

名称空间可以是全局的,也可以是位于另一个名称空间中,但不能处于代码块中,因此在默认情况下,在名称空间中声明的名称的链接性是外部的。

namespace Jack
{
	double pail;
	void fetch();
	int pal;
	struct Well{...};
}
namespace Jill
{
	double bucket(double n){...};
	double fetch;
	int pal;
	struct Hill{...}
}

任何名称空间中的名称都不会与其他名称空间中的名称发生冲突,因此Jack中的fetch与Jill中的fetch共存,Jill中的Hill可以与外部的Hill共存。
名称空间是开放的,可以把名称加入到已有的名称空间中

namespace Jill
{
	char * goose(const char*);//加入上述的Jill中
}

利用作用域解析运算符::来访问名称空间中的名称,使用名称空间限定该名称:

Jack::pail = 12.34;//赋值
Jill::Hill mole;//结构
Jack::fetch();//函数调用

在名称空间中声明的函数名的作用域为整个名称空间,因此声明和定义必须位于同一个名称空间中

using声明与using编译指令

using声明:

using Jill::fetch;

using编译指令:

using namespace Jill;

using声明只是让名称空间中的一个名称可用;using编译指令则是可以使用名称空间中的所有名称,可以认为using编译指令中包含了所有名称的using声明。

名称空间的其他特性

可以将名称空间声明进行嵌套:

namespace elements
{
	namespace fire
	{
		int flame;
		...
	}
	float water;
}

倘若要使用fire内部的名称,则using namespace elements::fire;
另外名称空间中也可以使用using编译指令和using声明:

namespace myth
{
	using Jill::fetch;
	using namespace elements;
	using std::cout;
	using std::cin;
}

倘若要访问fetch,因此可以这样myth::fetch;,也可以Jill::fetch;

未命名的名称空间

namespace 
{
	int count;
}

未命名的名称空间相当于提供了内部链接性的静态变量。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值