C++第五章数据的共享与保护
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
提示:以下是本篇文章正文内容,下面案例可供参考
5.1标识符的作用域与可见性
5.1.1作用域分类:
1.函数原型作用域:
如函数声明:double area (double radius)
2.局部作用域

3.类作用域
类的成员具有类作用域,其范围包括类体和非内联成员函数的函数体(函数原型在类体中声明,而函数体的实现是在类外,在类外实现的函数体依然是具有类作用域的)
4.命名空间作用域
我们写的小规模程序直接用using namespace std;
std 是 C++ 标准库的命名空间(namespace)。
把 std 这个命名空间里的所有名字(函数、类、对象等)都引入到当前作用域
这样写之后,你就可以直接写cout而不用写 std::cout
但是大程序不推荐这样用using namespace std;
#include <iostream>
using namespace std;
int count = 10; // 你定义的变量名 count
int main() {
cout << count << endl; // 这里 cout 是 std::cout,但 std 里面也有 count 函数
return 0;
}
这个程序编译器会报歧义错误,命名空间(namespace)就是 C++ 提供的一种解决命名冲突的方法。
语法长这样:
namespace MySpace {
int a = 42;
void print() { //定义一个print函数
std::cout << "a = " << a << std::endl;
}
}
如果要用 MySpace 里面的东西,可以这样:
int main() {
MySpace::print(); // 用命名空间限定符 ::
return 0;
}
也可以局部 using:
int main() {
using MySpace::a; // 只引入 a,不引入 MySpace 里的其他东西
std::cout << a << std::endl;
return 0;
}
5.限定作用域的枚举类

5.1.2 可见性
(好抽象的语言描述)
如果某个标识符在外层作用域中声明,且在内层中没有同一标识符的声明,则该标识符在内层可见。
对于两个嵌套的作用域,如果在内层作用域内声明了与外层作用域中同名的标识符,则外层作用域的标识符在内层不可见。
例题:故意写了两个重名的变量 i
#include<iostream>
using namespace std;
int i; // 全局变量(没有声明命名空间的,所以全局可用,进入主函数中也是可用的)
int main(){
i = 5; //为全局变量i赋值
{
int i; // 局部变量,局部作用域(这个i和全局变量的i名字一样但是是不同的变量,且存储的位置完全不一样的)
i = 7;
cout << "i=" << i <<endl; // 输出7
}
cout << "i=" << i << endl; // 输出5
return 0;
}
5.2 对象的生存期
5.2.1 静态生存期
静态生存期(比如全局变量):对象的生存期与程序运行期相同
在函数内部(局部作用域中),声明静态生存期对象前面要加上static
局部作用域中的静态变量,不会随着每次函数调用而产生一个新的副本,也不会随着函数返回而失效。也就是说,当一个函数返回后,下次再调用时,该变量还会保持上一回的值,即使发生了递归调用,也不会为该变量建立新的副本。
5.2.2 动态生存期
除了上述两种情况,其余的对象都具有动态生存期
5.3类的静态成员
5.3.1类的静态数据成员
类的静态数据成员是在类的所有对象间共享的,如果在定义类的成员的时候加上关键字static,那么这个成员就是静态的。静态成员在整个类中只有这一份,没有更多的拷贝,静态数据成员具有静态生存期,也就是整个代码运行的过程中,有的对象生成了有的对象消失了,但是静态数据成员始终在那里,当整个程序运行结束,静态数据成员的内存空间才被释放。
静态数据成员一般在类内声明,在类外初始化。C++11支持静态常量(const或constexpr修饰)类内初始化,此时类外仍可定义该静态成员,但不可再次初始化操作。
#include<iostream>
using namespace std;
class Point{
public:
Point(int initial_x = 0, int initial_y = 0){ //构造函数
x = initial_x;
y = initial_y;
count++;
}
Point(Point &p){ //复制构造函数
x = p.x;
y = p.y;
count++;
}
~Point(){ //析构函数
count--;
}
int getX(){
return x;
}
int getY(){
return y;
}
void showCount(){
cout << "Object count = " << count << endl;
}
private:
int x,y;
static int count; //声明静态数据成员,用于记录点的个数
};
int Point::count = 0; // 静态数据成员定义和初始化,使用类名限定:Point::
int main(){
Point a(4,5);
cout << "Point A:" << a.getX() << "," << a.getY() << " ";
a.showCount();
Point b(a); //使用复制构造函数
cout << "Point B:" << b.getX() << "," << b.getY() << " ";
b.showCount();
return 0;
}
运行输出:
Point A:4,5 Object count = 1
Point B:4,5 Object count = 2
当然如果你觉得类中的两个get函数写的很麻烦,也可以不写他们,直接在构造函数中加一个cout,然后主函数创建对象的时候就会自动输出这个点,但是当你想创建多个点的时候,还是要用get函数更合适:
#include<iostream>
using namespace std;
class Point{
public:
Point(int initial_x = 0, int initial_y = 0){ //构造函数
x = initial_x;
y = initial_y;
count++;
cout << "Point A:" << x << "," << y << " ";
}
Point(Point &p){ //复制构造函数
x = p.x;
y = p.y;
count++;
cout << "Point B:" << x << "," << y << " ";
}
~Point(){ //析构函数
count--;
}
void showCount(){
cout << "Object count = " << count << endl;
}
private:
int x,y;
static int count; //声明静态数据成员,用于记录点的个数
};
int Point::count = 0; // 静态数据成员定义和初始化,使用类名限定:Point::
int main(){
Point a(4,5);
a.showCount();
Point b(a); //使用复制构造函数
b.showCount();
return 0;
}
这个代码的输出和上面是一样的,但是为什么复制构造函数也要加上cout呢?难道复制构造函数不是复制的构造函数的所有的内容吗?哈哈哈哈哈我真的太搞笑了。
构造函数和复制构造函数是两个完全独立的函数,都用于创建对象,但使用场景不同:
普通构造函数:当你用参数直接创建新对象时调用,作用是从零开始初始化一个对象:Point a(4,5)
复制构造函数:当你用一个已经存在的对象创建新对象时调用,作用是:用已有对象的成员变量,初始化新对象:Point b(a)
这是两个不同的函数,各自的代码需要单独编写,复制构造函数不会自动复用构造函数的代码!
类的静态函数成员
同样用static来修饰,类外代码可以使用类名和作用域操作符来调用静态成员函数,可以直接使用对象来调用,如果是非静态函数成员,只能通过对象来调用。
#include<iostream>
using namespace std;
class Point{
public:
Point(int initial_x = 0, int initial_y = 0){ //构造函数
x = initial_x;
y = initial_y;
count++;
}
Point(Point &p){ //复制构造函数
x = p.x;
y = p.y;
count++;
}
~Point(){ //析构函数
count--;
}
int getX(){
return x;
}
int getY(){
return y;
}
static void showCount(){
cout << "Object count = " << count << endl;
}
private:
int x,y;
static int count; //声明静态数据成员,用于记录点的个数
};
int Point::count = 0; // 静态数据成员定义和初始化,使用类名限定:Point::
int main(){
Point::showCount();
Point a(4,5);
cout << "Point A:" << a.getX() << "," << a.getY() << " ";
a.showCount();
Point b(a); //使用复制构造函数
cout << "Point B:" << b.getX() << "," << b.getY() << " ";
Point::showCount(); //两种调用方法都可以
return 0;
}
运行结果:
Object count = 0
Point A:4,5 Object count = 1
Point B:4,5 Object count = 2
在还没有创对象的时候,一个对象还没有,可以使用类名和作用域操作符调用showCount,此时输出是0
5.4 类的友元
通过将一个模块声明为另一个模块的友元,(友元只能在类中声明)比如另一个函数是我的朋友,或者另一个类整个是我的朋友,声明为友元之后,授权给类外的其他函数或者另一个类,可以访问我的私有成员、保护成员。但是要尽量少用或不适用友元。
5.4.1友元函数
在类中用friend修饰声明一个函数原型,在他的函数体中能够通过对象名访问私有成员和保护成员。
5.4.2友元类
若一个类为另一个类的友元,则此类的所有成员都能访问对方类的私有成员。

5.5共享数据的保护
对于既需要共享又需要防止改变的数据应该声明为常类型(用const进行修饰)
对于不改变对象状态的成员函数应该声明为常函数
常类型:
(1)常对象:必须进行初始化,不能被更新
const 类名 对象名
(2)常成员:
用const进行修饰的类成员:常数据成员和常函数成员
(3)常引用:被引用的对象不能被更新
const 类型说明符 &引用名
(4)常数组:数组元素不能被更新(详见第六章)
类型说明符 const 数组名[大小]…
(5)常指针:指向常量的指针(详见第六章)
5.5.1常对象:
用const修饰对象
class A{
public:
A(int i, int j){
x=i;
y=j;
}
private:
int x,y;
};
A const a(3,4); //a是常对象,不能被更新
5.5.2常成员:
用const修饰的对象成员
常成员函数: 使用const声明的函数,常成员函数不更新对象的数据成员
格式:
类型说明符 函数名 (参数表) const
通过常对象只能调用它的常成员函数
常数据成员: 使用const说明的数据成员
//常成员函数举例
#include<iostream>
using namespace std;
class R{
public:
R(int r1, int r2) : r1(r1),r2(r2) {}
void print();
void print() const;
private:
int r1,r2;
};
void R::print(){
cout << r1 << ":" << r2 << endl;
}
void R::print() const{
cout << r1 << ":" << r2 << endl;
}
int main(){
R a(5,4);
a.print(); //调用void print()
const R b(20,52);
b.print(); //调用void print() const
return 0;
}
常对象b只能调用常成员函数void print() const,而不能调用普通成员函数void print(),并且常成员函数里面的代码是不能改变对象的数据成员的。
但是上面的代码中声明的两个函数功能是一样的啊,只是输出r1和r2,既然不能删除常成员函数,能不能删除普通成员函数呢?可以的,如果把void print()删掉,那么主函数中对象a.print();就会自动调用常成员函数,这样也是可以的。
//常数据成员举例
#include<iostream>
using namespace std;
class A{
public:
A(int i);
void print();
private:
const int a;
static const int b; //静态常数据成员
};
const int A::b = 10; //静态常数据成员在类外说明和初始化
A::A(int i) : a(i){} //常数据成员只能通过初始化列表来获得初值
void A::print(){
cout << a << ":" << b << endl;
}
int main(){
/*
建立对象a1 a2,并以100和0作为初值,分别调用构造函数,
通过构造函数的初始化列表给对象的常数据成员赋初值
*/
A a1(100),a2(0);
a1.print();
a2.print();
return 0;
}
5.5.3常引用:
如果在声明引用时用const修饰,被声明的引用就是常引用
常引用所引用的对象不能被更新(只读)
如果常引用做形参,便不会意外地发生对实参的更改。声明形式: const 类型说明符 &引用名
更多推荐


所有评论(0)