C++ 数据共享与保护
最近学习了 c++ 的常成员函数,常对象,常引用,静态函数成员,静态数据成员,学完之后感觉有些混乱于是写博客予以总结和记忆。
类中的静态成员
静态数据成员
顾名思义,就是类中的某些属性他是静态。这句话可以用下面这个例子来理解。
如果将类比作是一个工厂,对象是工厂中生产出的产品,那么静态数据成员就是存放在工厂中的,属于工厂,而不是属于每个产品
静态数据成员特点:
- 静态数据成员的一般用法是 类名::标识符
- 静态数据成员是用 static 修饰
- 静态数据成员在类中定义,在类外声明,之所以这样做是由于,在类被初始化时,非静态数据成员的存储空间与所属对象存储同时分配,而静态成员需要单独分配,也就是在类外部声明时分配
静态数据成员代码举例
#include <iostream>
using namespace std;
//点类的定义
class Point {
public:
Point(int x = 0, int y = 0) :x(x), y(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 << "点类的个数:" << count << endl;
}
private:
int x, y;
//静态数据成员,用于记录一共声明了多少个点类
static int count;
};
int Point::count = 0; //在类的外部初始化静态数据成员
主函数测试
int main()
{
Point a(4, 5); //定义点类 a,会使得 count 增加1
a.showCount();
Point b(a);
b.showCount();
return 0;
}
对于静态数据成员的访问,不应该是由某一个具体的对象来访问,因为,有时候可能连对象都没有声明就需要访问静态数据成员,所以访问静态数据成员应该是用类来访问比较合理,因为静态数据成员它不属于任何对象,是整个类所共有的。
静态函数成员
类中的一些函数他是静态的。
静态函数成员的特点:
- 可以通过类名和对象名调用,习惯用类名调用
- 可以直接访问类的静态数据和函数成员,
- 访问非静态的成员必须通过对象名
- 静态函数成员一般是用来访问静态数据成员的
解决这个问题的方法就是使用静态函数成员
将上面代码中 Point 类中的 showCount 方法改造成静态函数成员,因为它只访问静态数据成员
static void showCount() { //输出静态数据成员
cout << "点类的个数:" << count << endl;
}
改造之后就可以用 类名::静态函数名 这样的方法来访问静态数据成员了
Point::showCount();
注意:
- 静态成员函数可以直接访问该类的静态数据和函数成员,而访问非静态成员必须通过对象名,是因为对静态函数成员的调用是没有目的对象的,而调用非静态成员函数是隐含了当前对象在其中的,所以需要指定静态函数成员调用的是哪一个对象的非静态成员。
静态成员总结
- 静态数据成员
- 用 static 关键字来声明
- 习惯声明方式是 (static 数据类型 成员名)
- 习惯访问方式是 类名::标识符
- 需要在类中定义,类外初始化声明
- 静态函数成员
- 在函数名前加上 static 关键字
- 习惯使用方法是 类名::函数名
- 可以直接访问类的静态数据和函数成员,访问非静态成员,需要通过 对象名 . 属性 来访问
常类型的对象和数据
常类型的包括,常对象,常成员函数,常数据数据成员,常引用,是由 const 来修饰注意与 静态类型的数据区分
常对象
常对象:他的数据成员不能被改变,也就是说常对象必须进行初始化,而且不能被更新。
常对象特点:
- 数据成员不能被改变
- 常对象只能调用,对象中的常函数,不能调用其他非常函数
举例:
class A {
public:
A(int i,int j):x(i),y(j){}
void show() {
cout << "x:" << x << "y:" << y << endl;
}
private:
int x, y;
};
const A a(3, 4);
这里就相当于声明了一个常对象 a,a 的 x 和 y 被声明后就不能在改变了,当我使用 show() 方法时
a.show()
发现 a 这个对象不能调用 show 函数
这里就引申出一个常对象的特点,如果一个对象声明为常对象时,则通过该对象只能调用它的常成员函数,不能调用其他函数。
void show() const {
cout << "x:" << x << "y:" << y << endl;
}
当我将 show 函数声明为常函数时, 常对象就可以调用了。
常函数
常函数用 const 来修饰
习惯声明格式:类型说明符 函数名(参数表)const;
常函数的特点:
- 无论调用常函数的对象是否是常对象,在常函数调用期间,都被视作常对象,所以常成员函数不能修改对象的数据成员
- 两个个函数,函数名相同,是否添加了 const 修饰也可以作为函数重载的区分,而且,当时使用非常对象调用这个函数时,编译器一般选择不带 const 关键字的函数
代码举例
#include <iostream>
using namespace std;
class R {
public:
R(int r1,int r2):r1(r1),r2(r2){}
void print() {
cout << r1 << ":" << r2 << endl;
cout << "无 const" << endl;
}
void print()const {
cout << r1 << ":" << r2 << endl;
cout << " const" << endl;
}
private:
int r1, r2;
};
主函数调用
int main()
{
R r(5, 4);
r.print();
const R rr(12, 22);
rr.print();
return 0;
}
常数据成员
类中的数据也可以是常量,而且一旦说明了类中的某个数据成员是常数据成员,那么任何函数都不能对其赋值
常数据成员的特点:
- 用 const 来修饰
- 习惯声明方式:const 数据类型 常量名
- 常数据成员只能通过初始化列表来获得初值
- 特别的,类中的静态常量如果是整型或者枚举类型,那么可以直接在类中定义常量值
代码举例
class A {
public:
A(int i);
void show() {
cout << "a:" << a << " b:" << b << endl;
}
private:
const int a; //常数据成员
static const int b; //静态常数据成员
};
const int A::b = 10; //静态常数据成员需要在类外部声明
A::A(int i) :a(i){} //常数据成员只能通过初始化列表来获取初值
主函数调用
int main()
{
A a1(3),a2(0);
a1.show();
a2.show();
return 0;
}
运行结果
b 是静态常数据成员,也就是类的属性,一经初始化后所有的对象共有这个属性,而且他是常类型的,他的值也不能在改变。
常引用
可以参照复制构造函数来学习。
常引用特点:
- 常引用引用的对象不能被更新
- 常引用可以绑定到常对象
- 不能修改数据成员
终!