- 单独编译
- 存储持续性(数据保留在内存中的时间长度)、作用域和链接性(哪一部分可以访问数据)
- 定位new运算符
- 名称空间
将原来的程序分成三部分:
- 头文件:包含结构声明和函数原型
- 源代码文件:包含与结构有关的函数定义
- 源代码文件:包含调用与结构有关的函数(main)
头文件中常包含的内容:
- 函数原型
- 使用#define或const定义的符号常量
- 结构声明、类声明、模板声明
- 内联函数
//程序清单 9.1 coordin.h
#pragma once
#ifndef __COORDIN_H__
#define __COORDIN_H__
//防止多次调用头文件时重复声明
//结构
struct polar
{
double distance;
double angle;
};
struct rect
{
double x;
double y;
};
//函数原型
polar rect_to_polar(rect xypos);
void show_polar(polar dapos);
#endif // !__COORDIN_H__ //勿忘
//程序清单 9.2 file1.cpp #include#include "coordin.h" //包含头文件 using namespace std; int main(void) { rect rplace; polar pplace; cout << "Enter the x and y values: "; while (cin >> rplace.x >> rplace.y) { pplace = rect_to_polar(rplace); show_polar(pplace); cout << "Next two numbers (q to quit): "; } cout << "Done!n"; return 0; }
//程序清单 9.3 file2.cpp #include9.2 存储持续性、作用域和链接性//<>去系统目录找 #include #include "coordin.h" //""去工程目录找 using namespace std; polar rect_to_polar(rect xypos) { polar answer; answer.distance = sqrt(xypos.x * xypos.x + xypos.y * xypos.y); answer.angle = atan2(xypos.y, xypos.x); return answer; } void show_polar(polar dapos) { const double Rad_to_Deg = 57.29577591; cout << "distance = " << dapos.distance << ", angle = " << dapos.angle * Rad_to_Deg << " degreesn"; }
C++使用3种不同的方案来存储数据,区别在于数据保留在内存的时间
- 自动存储持续性:(局部变量),函数定义中声明的变量,程序执行时创建,完成后释放。
- 静态存储持续性:(全局变量),函数定义外的变量和使用关键字static定义的变量,整个程序运行过程都存在。
- 动态存储持续性:使用new分配的内存一直存在,直到使用delete释放或程序结束为止。有时称为自由存储或堆。
- 作用域描述了名称在文件的多大范围可见。
- 链接性描述了名称如何在不同单元间共享。
- 链接性为外部的名称可在文件间共享,链接性为内部的名称只能由一个文件中的函数共享。
- 自动变量的名称没有链接性,不能共享。
//程序清单 9.4 #include9.2.4 静态持续性、外部链接性using namespace std; void oil(int x); int main(void) { int texas = 27; int years = 2022; cout << "In main(), texas = " << texas << ", &texas = " << &texas << endl; cout << "In main(), years = " << years << ", &years = " << &years << endl; oil(texas); cout << "In main(), texas = " << texas << ", &texas = " << &texas << endl; cout << "In main(), years = " << years << ", &years = " << &years << endl; return 0; } void oil(int x) { int texas = 5; cout << "In oil(), texas = " << texas << ", &texas = " << &texas << endl; cout << "In oil(), x = " << x << ", &x = " << &x << endl; //main中texas = 27的拷贝,按值传递 { int texas = 113; //仅在代码块中作用 cout << "In block(), texas = " << texas << ", &texas = " << &texas << endl; //texas就近原则 cout << "In block(), x = " << x << ", &x = " << &x << endl; } cout << "Post block, texas = " << texas << ", &texas = " << &texas << endl; }
链接性为外部的变量简称为外部变量(全局变量),存储持续性为静态,作用域为整个文件。
#pragma once #ifndef __SUPPORT_H__ #define __SUPPORT_H__ #includeextern double warming; //声明外部变量,来自external.cpp void update(double dt); void local(void); #endif // !__SUPPORT_H__
//程序清单 9.5 #include#include "support.h" using namespace std; double warming = 0.3; //外部(全局)变量赋值 int main(void) { cout << "Global warming is " << warming << " degrees.n"; update(0.1); cout << "Global warming is " << warming << " degrees.n"; local(); cout << "Global warming is " << warming << " degrees.n"; return 0; }
//程序清单 9.6
#include "support.h"
using namespace std;
void update(double dt)
{
warming += dt;
cout << "Updating global warming to " << warming << " degrees.n";
}
void local(void)
{
double warming = 0.8; //局部变量,只在local内部可见
cout << "Local warming = " << warming << " degrees.n";
cout << "But global warming = " << ::warming << " degrees.n";
//::作用域解析运算符,放在变量前面,表示使用全局版本
}
local()表明,定义与全局变量同名的局部变量后,局部变量将隐藏全局变量。
cout << "But global warming = " << ::warming << " degrees.n";
//::作用域解析运算符,放在变量前面,表示使用全局版本
9.2.5 静态持续性、内部链接性将static限定符用于作用域为整个文件的变量时,该变量的链接性将为内部的。
//file1
int errors = 20; //常规外部变量
...
---------------------------------------------------------
//file2
static int errors = 5; //只作用于file2
void froobish()
{
cout << errors; //errors == 5
//静态变量隐藏常规外部变量
...
}
如果将作用域为整个文件的变量变为静态的,就不必担心其名称与其他文件中的作用域为整个文件的变量发生冲突。
//程序清单 9.7 twofile1.cpp #includeusing namespace std; //具有外部链接性的全局变量 int tom = 3; int dick = 30; //只作用于twofile1.cpp的静态全局变量 static int harry = 300; void remote_access(void); int main(void) { cout << "main() reports the following addresses:n"; cout << &tom << " = &tom, " << &dick << " = &dick, " << &harry << " = &harryn"; remote_access(); return 0; }
//程序清单 9.8 #include9.2.6 静态存储持续性、无链接性using namespace std; extern int tom; //声明外部变量 static int dick = 20; //静态全局 int harry = 200; //具有外部链接性的全局变量 void remote_access(void) { cout << "remote_access() reports the following addresses:n"; cout << &tom << " = &tom, " << &dick << " = &dick, " << &harry << " = &harryn"; }
- 无链接性的局部变量:将static限定符用于在代码块中定义的变量。
- 导致局部变量的存储持续性为静态的。
- 如果初始化了静态局部变量,则程序只在启动时进行一次初始化。
//程序清单 9.9 #include9.2.7 说明符和限定符using namespace std; const int ArSize = 10; void strcount(const char* str); int main(void) { char input[ArSize]; char next; cout << "Enter a line:n"; cin.get(input, ArSize); while (cin) { cin.get(next); while (next != 'n') //超过10个,多余消耗 cin.get(next); strcount(input); cout << "Enter next line (empty line to quit):n"; cin.get(input, ArSize); } cout << "Byen"; return 0; } void strcount(const char* str) { static int total = 0; //只初始化一次 int count = 0; //每次都被初始化为0 cout << """ << str << "" contains "; while (*str++) count++; total += count; cout << count << " charactersn"; cout << total << " characters totaln"; }
- volatile:即使程序代码没有对内存单元进行修改,其值也可能发生变化。不将变量声明为volatile,从缓冲区取值,变量不变;将变量声明为volatile,从端口取值,取时刻变化的变量。
- mutable:指出即使结构(或类)变量为const,其某个成员也可以被修改。
struct data { char name[30]; mutable int accesses; ... }; const data veep = { "AAA", 0, ... }; strcpy(veep.name, "BBB"); //不允许 veep.accesses++; //允许 - const:const全局变量的链接性为内部。在C++看来,就像使用了static。只在当前文件内可用。const int figures = 10; == static const figures = 10;
- C++不允许在一个函数中定义另外一个函数,因此所有函数的存储持续性都自动为静态,即在整个程序执行期间都存在。
- 默认情况下,函数的链接性为外部的,即可以在文件间共享。
- 实际上,可以用关键字externa指出函数是在另一个文件中定义的,不过这可选。
- 还可以使用关键字static将函数的链接性设置为内部,使之只能在一个文件中使用。这意味着可以在其他文件中定义同名的函数,而在静态函数文件中,静态函数将覆盖外部定义。
动态内存由运算符new和delete控制,而不是由作用域和链接性规则控制。
1.使用new运算符初始化
- 如果要为内置的标量类型(如int或double)分配存储空间并初始化,可在类型名后面加上初始值,并用括号括起。
int * pi = new int (6); double * pd = new double (99.99);
- 如果要初始化常规结构或数组,需要用大括号的列表初始化
struct where { double x; double y; double x; }; where * one = new where { 2.5, 5.3, 7.2 }; int * ar = new int[4] { 2, 4, 6, 7 }; //C++11中,列表初始化也可以用于单值变量 int * pi = new int {6}; double * pd = new double {99.99};
2.new失败时
引发异常std::bad_alloc
3.运算符、函数和替换函数(new和delete对应有函数)
//分配函数
void * operator new(std::size_t);
void * operator new [] (std::size_t);
//释放函数
void operator delete (void *);
void operator delete [] (void *);
//使用的是第11章的运算符重载。td::size_t是一个typedef
4.定位new运算符
- new负责在堆中找到一个满足要求的内存块,定位new运算符能够指定要使用的位置
- 使用定位new运算符,需要包含头文件new
//程序清单 9.10 #include9.3 名称空间 9.3.2 新的名称空间特性#include using namespace std; const int BUF = 512; const int N = 5; char buffer[BUF]; int main(void) { double* pd1, * pd2; int i; cout << "Calling new and palcement new:n"; pd1 = new double[N]; pd2 = new (buffer) double[N]; //以buffer所在地址开始 for ( i = 0; i < N; i++) pd2[i] = pd1[i] = 1000 + 20.0 * i; cout << "Memory addresses:n"; cout << "pd1 = " << pd1 << ", pd2 = " << pd2 << ", buffer = " << (void*)buffer << endl; //(void *)buffer 强制类型转换 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]; //找新地址 pd4 = new (buffer) double[N]; //覆盖旧数据 for (i = 0; i < N; i++) pd3[i] = pd4[i] = 1000 + 40.0 * i; cout << "Memory addresses:n"; cout << "pd3 = " << pd3 << ", pd4 = " << pd4 << ", buffer = " << (void*)buffer << endl; cout << "Memory contents:n"; for (i = 0; i < N; i++) { cout << pd3[i] << " at " << &pd3[i] << "; "; cout << pd4[i] << " at " << &pd4[i] << endl; } cout << "nCalling new and placement new a third time:n"; delete[] pd1; //释放pd1 pd1 = new double[N]; //重新开辟 pd2 = new (buffer + N * sizeof(double)) double[N]; //后移5个double长度地址 for (i = 0; i < N; i++) pd1[i] = pd2[i] = 1000 + 60.0 * i; cout << "Memory addresses:n"; cout << "pd1 = " << pd1 << ", pd2 = " << pd2 << ", buffer = " << (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; } delete[]pd1; //只释放传统new delete[]pd3; 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::pail = 12.34; Jill::Hill mole; Jack::fetch();
1.using声明和using编译指令
namespace Jill
{
double bucket(double n) { .. }
double fetch;
int pal;
struct Hill { ... };
}
char fetch; //全局fetch
int main()
{
using Jill::fetch; //using声明
double fetch; //错误,重名
cin >> fetch; //代替Jill::fetch,覆盖全局fetch
cin >> ::fetch; //全局fetch
...
}
using声明使一个名称可用,using编译指令使所有名称可用 。
using编译指令:using namespace Jack;
2.using编译指令和using声明的比较
namespace Jill
{
double bucket(double n) { .. }
double fetch;
int pal;
struct Hill { ... };
}
char fetch; //全局fetch
int main()
{
using namespace Jill; //using 编译指令
Hill Thrill; //Jill::Hill结构
double water = bucket(2); //Jill::bucket()
double fetch; //没有错,局部fetch覆盖Jill::fetch
cin >> fetch; //局部fetch
cin >> ::fetch; //全局fetch
cin >> Jill::fetch; //Jill::fetch
...
}
int foom
{
Hill top; //错误
Jill::Hill crest; //有效
}
假设名称空间和声明区域定义了相同的名称:
- 使用using声明,发生重名冲突;
- 使用using编译指令,局部版本隐藏名称空间版本
- 使用using声明比using编译指令更安全
3.名称空间声明嵌套
namespace element
{
namespace fire
{
int flame;
...
}
float water;
}
- flame指的是element::fire::flame
- using namespace element::fire;
//程序清单 9.11 namesp.h #pragma once #ifndef __NAMESP_H__ #define __NAMESP_H__ #includenamespace pers { struct Person { std::string fname; std::string lname; }; void getPerson(Person& rp); void showPerson(const Person& rp); } namespace debts { using namespace pers; struct Debt { Person name; double amount; }; void getDebt(Debt& rd); //结构体引用 void showDebt(const Debt& rd); double sumDebt(const Debt ar[], int n); } #endif // !__NAMESP_H__
//程序清单 9.12 #include#include "namesp.h" namespace pers { using std::cout; using std::cin; void getPerson(Person& rp) { cout << "Enter first name: "; cin >> rp.fname; cout << "Enter last name: "; cin >> rp.lname; } void showPerson(const Person& rp) { std::cout << rp.lname << ", " << rp.fname; } } namespace debts { void getDebt(Debt& rd) { getPerson(rd.name); std::cout << "Enter debt: "; std::cin >> rd.amount; } void showDebt(const Debt& rd) { showPerson(rd.name); std::cout << ": $" << rd.amount << std::endl; } double sumDebt(const Debt ar[], int n) //结构体数组 { double total = 0; for (int i = 0; i < n; i++) total += ar[i].amount; return total; } }
#include#include "namesp.h" int main(void) { using debts::Debt; using debts::showDebt; Debt golf = { {"Benny", "Goatsniff"}, 12.0 }; showDebt(golf); return 0; }



