标识符的作用域与可见性
作用域包括:
1)函数原型作用域
C++程序中最小的作用域。在函数原型声明时形式参数的作用范围就是函数原型作用域。,这就是为什么函数声明的时候可以只写类型不写变量名
double area(double radius) //radius为变量名,可以不写
2)局部作用域(块作用域)
1)函数形参列表中的形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为止。
2)函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的大括号为止。
具有局部作用域的变量也称局部变量
3)类作用域
包括类体和类外成员函数体,所以在类外定义非构造函数的成员函数的时候可以直接调用类内公有成员数据
4)文件作用域
如果标识符不在上述作用域,则存在于文件作用域
5)命名空间作用域
可见性:能否引用标识符
如果标识符在外层中声明,且内层无相同标识符,则该标识符在内层可见
对于嵌套标识符,如果内层与外层同名,则内层不可见
对象生存周期
- 全局区:
全局变量(任何函数之外的变量):在程序启动时分配,在程序结束时销毁
局部静态变量、类静态数据成员:第一次使用前分配内存,在程序结束时销毁 - 栈内存:函数(形)参数值、局部变量,进入其定义所在的程序块时被创建,在离开块时销毁
- 堆内存:new分配空间,delete释放空间
类的静态数据成员
- 静态数据成员被类的所有对象共享,包括该类的派生类对象,基类对象和派生类对象共享基类的静态数据成员
- 静态数据成员可以作为成员函数的默认形参,而普通数据成员则不可以
- 静态数据成员在const函数中可以修改,而普通的数据成员是万万不能修改的
例子:具有静态数据成员-点个数-的Point类,需要统计总的Point个数,但是每个Point对象不应该包含这个个数
#include<iostream>
using namespace std;
class Point {
public:
Point(int x, int y) :x(x), y(y) { //构造函数使用初始列表初始化x,y
count++;
}
Point(const Point &p) { //复制构造函数
x = p.x; y = p.y; count++;
}
~Point() { count--; } //析构函数中点数减一
int getX() { return x; }
int getY() { return y; }
void showCount() {
cout << "Point count:" << count << endl;
}
private:
int x, y;
static int count;
};
int Point::count = 0; //类外初始化静态变量
int main()
{
Point a(4,5);
cout << "Point A" << a.getX()<<","<< a.getY()<<endl;
a.showCount();
Point b(a);
cout << "Point A" << b.getX() <<" ," << b.getY() << endl;
b.showCount();
return 0;
}
类的静态函数成员
- 静态成员函数只能访问静态成员数据、其他静态成员函数和类外部的其他函数(这是因为静态成员在定义类还未创建对象时已经开辟内存空间,非静态成员变量只有在类对象建立以后才可以调用)
- 静态成员函数没有this指针,也就是说静态成员函数不能使用修饰符(也就是函数后面的const关键字)
类的友元
友元(frend)机制允许一个类将对其非公有成员的访问权授予指定的函数或者类,友元的声明以friend开始,它只能出现在类定义的内部,友元声明可以出现在类中的任何地方:友元不是授予友元关系的那个类的成员,所以它们不受其声明出现部分的访问控制影响。
- 友元函数
友元函数是指某些虽然不是类成员函数却能够访问类的所有成员的函数 - 友元类
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息,包括私有成员和保护成员
- 友元关系不能被继承。
- 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
- 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明。
//类内声明友元函数,用于计算两点的距离
friend float dist(Point &a,Point &b);
//类外定义全局函数,但是可以调用类内的私有变量
float dist(Point &a,Point &b){
double x=a.x-b.x;
double y=a.y-b.y;
return static_cast<float>sqrt(x*x+y*y);
}
友元类举例:
B的私有成员为A类,在B的set函数中想直接改变A类的数据,可在A内把B声明为友元
常类型const
- const修饰指针 如果const位于星号的左侧,则const就是用来修饰指针所指向的变量,即指针指向的对象为常量;如果const位于星号的右侧,const就是修饰指针本身,即指针本身是常量。
const int *a =&b //a指向的对象为常量
int const *a =&b //a指向的对象为常量
int* const a= &b //a指针为常量
const int* const a= &b //a指针和a指向的内容均为常量
- 函数中使用const
1)const修饰函数参数
a.传递过来的参数在函数内不可以改变(无意义,因为Var本身就是形参)
void function(const int Var);
b.参数指针所指内容为常量不可变
void function(const char* Var);
c.参数指针本身为常量不可变(也无意义,因为char* Var也是形参)
void function(char* const Var);
d.参数为引用,为了增加效率同时防止修改。修饰引用参数时:
void function(const Class& Var); //引用参数在函数内不可以改变
2)const修饰成员函数
const修饰类的成员函数,则该成员函数不能修改类中任何非const成员变量,也不能调用类中任何非const成员函数。一般写在函数的最后来修饰。
class A
{
…
void function()const; //常成员函数, 它不改变对象的成员变量.
//也不能调用类中任何非const成员函数。
}
3)const修饰类对象/对象指针/对象引用
const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。
const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
class AAA
{
void func1();
void func2() const;
}
const AAA aObj;
aObj.func1(); ×
aObj.func2(); 正确
const AAA* aObj = new AAA();
aObj-> func1(); ×
aObj-> func2(); 正确
1)const常用于函数输入引用形参修饰,使得形参引用的实参不被改变,相当于一个只读的,也多用于友元函数
2)在定义对象的时候,如果某个函数只是单纯的输出,不改变对象的状态,可以在该函数定义时加const限定,这样做可以方便调用常函数处理普通对象和常对象
多文件结构
extern:使得变量可以多文件调用
namespace:限制当前文件变量和函数只能在当前文件使用
条件编译
#if 表达式
....
#endif
#ifdef 标识符
...
#endif