1 Memory Model¶
1.1 变量和动态内存分配¶
C++中的变量类型¶
(1)global variable 全局变量,存储在全局变量区,可以在不同的cpp文件之间共享,可以使用关键字extern
来使用别的cpp文件中的全局变量
Note
在 1.cpp 中定义全局变量 int a
, 在 2.cpp 中使用 extern int a
调用全局变量
(2)static global variable 静态全局变量,不能在cpp文件之间共享
(3)local variable 存储在栈区上
(4)static local variable 静态局部变量
- 存储在全局变量区
- 在初次使用的时候初始化,keeps its value between visit to the function
(5)allocated variable 动态分配的变量 - 存储在内存的堆结构中
Where are they in memory?
stack
- local vars
heap
- dynamically allocated vars.
code/data
-
global vars
-
static global vars
-
static local vars
C++动态内存分配¶
new用于动态分配内存给变量,如new int
,new double[1000]
- 与malloc的区别:malloc不执行类的构造函数,而new出新的对象的时候会执行对象的构造函数
new Class_Name[x]
会执行x次Class_Name的构造函数
内存泄漏 Memory Leak
上面这段代码中一开始为指针p分配了一段内存空间并赋值了123,但是第三行代码又为p赋值了一段新的内存空间,原来的内存空间存储了123,但是这一段内存空间已经没有指针指向,因此不能访问,也不能删除,造成了内存泄漏
delete用于删除动态分配的内存
- 用法
delete p;
delete[] p;
- 和new类似,delete会执行所删除对象的构造函数,不能delete没有定义过的变量,同一个变量不能delete两次
同一个变量不能delete两次
两个指针p1,p2指向同一个数据,如果p1被delete了,p2也不能访问原本p1指向的变量的值,因为delete删除的是内存里的数据
指向同一个数据的两个指针实际上只是两个不同的变量名而已,delete删除的不是变量名而是数据
1.2 引用 Reference¶
a new type in C++, 相当于给变量取了一个别名。
Defining references¶
定义引用的方式有两种,对于一般的引用如下定义即可,
另一种就是在函数参数列表种或者构造函数中定义即可。
基本的用法如下,就相当于给 X 取了一个别名Y
int X = 47;
// Y is a reference to X, X and Y now refer to
// the same variable
int &Y = X;
cout << "Y = " << Y; // prints Y = 47
Y = 18;
cout << "X = " << X; // prints X = 18
Rules of references¶
(1)不能定义空引用,引用必须连接到一块合法的内存,下面展示了两种定义方式
int x = 3;
int& y = x;
const int& z = x;
void f (int& x);
f(y); // initialized when function is called
(2)不像指针一样可以重绑
(3)The target of a non-const reference must be an lvalue. 非常值引用不可以是左值
Type restrictions¶
No references to references
No pointers to references, but reference to pointer is ok
No arrays of references
1.3 const 类型¶
用于定义常量类型,如const int x=12345;
, const类型的变量在初始化之后就不能改变其值,const型变量不能在连接单元外使用
const int x = 123;
x = 27; // illegal!
x++; // illegal!
int y = x; // ok, copy const to non-const
y = x; // ok, same thing
const int z = y; // ok, const is safer
在C++中,如果你在一个文件中定义了一个const
变量而不使用extern
关键字,那么这个const
变量默认具有内部链接。这意味着该const
变量只能在定义它的文件内访问,其他文件无法访问它,即使你尝试通过extern
声明来引用它。这种行为有助于避免不同编译单元之间的命名冲突。然而,如果你希望一个const
变量能够在多个源文件之间共享,你可以使用extern
关键字显式地声明这个const
变量具有外部链接,这样编译器就会为这个const
变量分配存储空间,允许其他源文件通过extern
声明来访问这个变量。
编译器优化:将const
值保存在符号表中:当涉及到const
变量时,如果它们不被取地址(即没有使用&
操作符获取其地址)并且被视为常量表达式,编译器通常会尝试优化这些变量的处理方式。具体来说,编译器可能会选择不在内存中为这些const
变量分配实际的存储空间,而是直接将其值保存在符号表中,并在编译时替换所有对这个const
变量的引用为其实际值。
Run-time constants¶
数组的定义时的长度值必须在编译期就已知,所以宏定义中的常数可以作为数组长度,而int n; int a[n]
这样的语法就是错误的,不过现在似乎有了编译器优化,在Dev-cpp中这样写也可以通过编译
Pointers with const
¶
注意区别下面两个式子
-
常量指针:
char * const p = "abc";
不能赋予这个指针新的地址,相当于地址是const类型,但是p指向的值可以改变 -
const char* p = "abc";
这种情况下p指向的是一个const char类型的值,因此指向的值不能改变,而指针指向的对象可以改变
//第一种情况是q是一个const指针,但是q指向的东西可以变
char * const q ="abc";
*q='c'; // OK
q++; // Error!
//第二种情况下,(*q)是一个const的值,此时q所指向的值不能变
const char *p = "abc";
*p='c';//error!
//区别这三种东西
string p1="zyc";
const string *p=&p1;
string const* p=&p1;//与第一个式子是相同的
string *const p=&p1;
不能将const类型的变量的地址赋值给非const变量的指针,因为可能会带来const变量的改变,这是const类型的变量不允许的
int i;
const int ci = 3;
int* ip;
const int* cip;
ip = &i;
ip = &ci; // Error, 因为ci是const而ip指向的对象并不是const
cip = &i;
cip = &ci;
*ip = 54; // always legal
*cip = 54; // never legal
可以把非const类型的值赋给对应的const型变量,函数中可以将参数设置为const类型表明这些参数在函数中不能被修改原本的值,也可以将返回值类型设置为const表示返回值不能被修改
int f3() { return 1; }
const int f4() { return 1; }
int main() {
const int j = f3(); // works fine
int k = f4(); // this works fine too
}
关于函数返回值 const 的使用
这样使用返回值的方法是对的,这只是一个拷贝
但是如果我们尝试修改 const
返回值的时候,就会出现编译错误;或者将 const
返回值绑定到非 const
引用的时候,也会出现编译错误
String literals¶
char * s="Hello World!";
实质上是const char *
类型,不要去修改s中的内容,这是一种未定义的行为(undefined behavior)
最好写成 char s[]="Hello World!";
C++ 编译器为了节省内存,会对相同的字符串字面量进行优化,将它们合并到同一个内存地址上。这叫做 字符串池(string pooling) 或 字符串驻留(string interning)。也就是说,虽然你写了两次 "Hello World"
,但编译器可能只会为它分配一次内存空间,然后让 s1
和 s2
都指向这个唯一的实例。
#include<iostream>
using namespace std;
int main()
{
const char *s1="Hello World";
const char *s2="Hello World";
cout<<(void*)s1<<endl;
cout<<(void*)s2<<endl;
return 0;
//输出的结果是s1和s2的地址,他们的结果是一样的
}
Conversions¶
Can always treat a non-const value as const
You cannot treat a constant object as non-constant without an explicit cast const_cast
,不可以把 const 当作 non-const 来使用,除非进行转换。