C++内存模型与名称空间(存储持续性、作用域、链接性、动态内存分配、命名空间)

1、静态持续家族三大成员

/*A--!静态持续性、外部链接性--*/
//testMain.cpp
int intValue;
/**如此声明表示:静态持续性、外部链接性---
外部变量定义 默认进行零初始化 多文件中只能有一个该变量的定义(单定义规则)
*/
//testPage1.cpp
extern int intValue;//使用intValue前 应如此声明
void testFunction()
{
    int intValue=1;//此处自动变量的定义将会在该自动变量的可见范围内覆盖普通外部变量的值(即当前语句块范围内)
}
/*B--!静态持续性、内部链接性--*/
//testMain.cpp
int intValue;//外部变量

//testPage1.cpp
static int intValue=10;
/**如此声明表示:静态持续性、内部链接性---
static 将变量链接性修饰为内部的,仅当前文件可见。故与外部变量intValue不冲突。当前文件该变量的值为静态外部变量的值。
我们只是称呼其为静态外部变量以和外部变量相对应,但static修饰后,其并不具有外部链接性。
*/
/*C--!静态持续性、无链接性--*/
//testMain.cpp
int TestFunction()
{
    static int num = 0;
    /**如此声明表示:静态持续性、无链接性---
    static 将其存储型修饰为静态的。故其在其所在代码块不处于活动状态时仍然存在,仅在其所在代码块内可用。
    变量仅在程序启动时进行一次初始化
    */
    return ++num;
}
int main() 
{
    std::cout << TestFunction() << std::endl;//输出值为1
    std::cout << TestFunction() << std::endl;//输出值为2
    std::cout << TestFunction() << std::endl;//输出值为3
}
/*
    **静态存储持续性:在函数定义外定义的变量和使用关键字static定义的变量的存储持续性都为静态。
    它们在程序整个运行过程中都存在。
    **动态存储持续性:用new运算符分配的内存将一直存在,直到使用delete运算符将其释放或程序结束为止。
    这种内存的存储持续性为动态,又称为heap(堆)。
*/

2、说明符/限定符

/*A、cv-限定符*/
/*  a、const;
        用const修饰后的内存进行初始化后就不能在对其进行更改。*/
/*  b、volatile;
        volatile表明,即使程序代码没有对内存单元进行修改,其值也可能发生改变。*/
/*B、mutable
mutable表名,即使结构(或类)变量声明为const,其某个成员也可以被修改。如下面代码:
*/
struct data
{
    char name[30];
    mutable int accesses;
}
const data veep={"test data 000"};
strcpy(veep.name,"test data 111");//不允许
veep.accesses++;//允许

3、函数链接性

c++不允许在一个函数中定义另一个函数,因此所有函数的存储持续性都为静态的,即在整个程序
执行期间都存在。默认情况下函数链接性为外部的,即可在文件间共享。函数也遵循”单定义规则”,可使用static关键字将函数修饰为
内部链接性的,即仅可在当前文件中访问。这意味着可以存在全局函数与static修饰的函数重名,在static修饰的函数的文件中,全局函数
将会被覆盖。
内联函数不受单定义规则约束 c++约定同一个函数的内联定义都必须相同;
例:
testMain.cpp

#include <iostream>

extern int GetIntValue();//有无extern的效果是一样的,可以理解为声明函数默认添加extern修饰符
int main() 
{
    std::cout << GetIntValue() << std::endl;
}

testPage1.cpp

int GetIntValue() 
{
    int intValue = 10;
    return intValue;
}

4、动态内存分配

动态内存由运算符new 和 delete控制,而不是由作用域和链接性规则控制。动态内存的
分配和释放顺序取决于new 和 delete在何时以何种方式被使用。

a、使用new运算符初始化
int *pi=new int(6);
int *pin=new int{6};//c++11
double *pd=new double(9.9);
double *pdo=new double{9.99};//c++11
struct data{double x;double y;double z;};
data* tempData=new data{1.1,2.5,3.2};//c++11
//new可能找不到请求的内存量,这种情况下将会引发std::bad_alloc异常。
b、new:运算符、函数和替换函数

运算符new和new[]分别调用如下函数:

void *  operator new(std::size_t);
void *  operator new[](std::size_t);
/*这些函数被称为分配函数(alloction function),它们位于全局名称空间中。
对于delete 和 delete[]调用的释放函数(deallocation function)也是如此。*/
void operator delete(void *);
void operator delete[](void *);

例:

int *pi=new int;/*将被转换为*/ int *pi=new (sizeof(int));
int *pa=new int[40];/*将被转换为*/ int *pa=new (40*sizeof(int));
/*同理*/
delete pi;
/*将转换为如下函数调用*/
delete(pi);
c、定位new运算符
#include <iostream>
int main() 
{
    char* buffer1 = new char[40];
    char buffer2[40] = "hello world";;

    int  *p1, *p2, *p3;
    p1 = new int(4);
    p2 = new (buffer1) int;
    p3 = new (buffer2) int;
    *p3 = 5;
    std::cout << p1 << std::endl << std::endl;

    std::cout <<"buffer1内存地址为:"<< static_cast<void*>(buffer1) << std::endl;
    std::cout << "指针p2内存地址为:"<<std::hex << p2 << std::endl << std::endl;;

    std::cout <<"buffer2内存地址为:"<< static_cast<void*>(buffer2)<< std::endl;
    std::cout << "指针p3的内存地址为:"<<std::hex<<p3 << std::endl;
    //delete 可释放由new 分配的内存故 delete buffer3将会出错
    delete p1, p2, p3;
    //释放该内存后,其指向的内存将为不活动的。再次分配前无法使用。
    //delete buffer3;  出错
}

程序输出结果:
指针p1的内存地址为:0108EA40

buffer1内存地址为:010842D8
指针p2内存地址为:010842D8

buffer2内存地址为:0018FA00
指针p3的内存地址为:0018FA00

//定位new函数不可替换,但可重载。
int * p1=new (buffer) int;/*将会被转换为*/void* new (sizeof(int),buffer);
int * p2=new (buffer) int[40];/*将会被转换为*/void* new(40*sizeof(int),buffer);

5、名称空间

名称空间可以是全局的,也可以位于另一个名称空间中,但不能位于代码块中。因此,在默认
情况下,在名称空间中声明的名称的链接性为外部的(除非它引用了常量)

a、创建命名空间
namespace space1
{
    float value_f1=0.5f;
    int value_i1=1;
}
b、使用命名空间
case 1 通过::区域解析符进行访问  space1::value_f1;这样的方式称为限定的名称
case 2 通过using 指令
        using 编译指令  使整个命名空间的标识符可用   using namespace space1;
        using 声明指令  使特定的标识符可用   using space1::value_f1;

例:

float value_f1=0;//允许       
int main() 
{
    using std::cout;
    using space1::value_f1;
    //float value_f1 = 0.5f; //不允许同名
}
/*将using声明放在局部声明区域中,故当前局部作用域不能再有同名value_f1变量。
同时也会隐藏全局变量value_f1(如果有);*/
//float value_f1 = 0;//不允许
int main() 
{
    using std::cout;

    using namespace space1;
    float value_f1 = 0.5f;
    cout << value_f1;
}
/*将using编译指令放在局部声明区域中,会将名称空间中的标识符视为函数外声明的,但除此局部声明区域外(此函数外),别的函数是访问不到的。故全局value_f1的声明是不允许的。*/
c、未命名的名称空间
namespace  
{
    float value_f1=0.5f;
}
//static float value_f1 = 0.1f;
int main() 
{
    using std::cout;
    cout << value_f1;
}
//未命名的名称空间中的标识符为内部链接的,相当于 static 修饰的变量

总结:c++鼓励程序员在开发程序时使用多个文件。一个有效的组织策略是,使用头文件来定义用户类型,为操纵用户类型的函数提供函数原型;并将函数定义在一个独立的源代码文件中。头文件和源代码文件一起定义和实现了用户定义的类型极其使用方式。最后,将main()和其他使用这些函数的函数放在第三个文件中。


本文参考文献:《c++ primer plus 第6版》。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值