一、标识符的作用域与可见性
作用域
作用域是一个标识符在程序正文中有效的区域。
1. 函数原型作用域
最小的作用域,在函数原型声明时,形参的作用范围就是函数原型作用域。例如:
double area(double radius);
标识符 radius 的作用域就在函数 area 形参列表的左右括号之间,在程序的其他地方不能引用这个标识符。因此,标识符radius的作用域称做函数原型作用域。
2. 局部作用域
例如:
函数形参列表中形参的作用域,从形参列表中的声明处开始,到整个函数体结束之处为止。函数体内声明的变量,其作用域从声明处开始,一直到声明所在的块结束的大括号 ‘}’ 为止。
具有局部作用域的变量也称为局部变量。
3. 类作用域
(1)如果在X的成员函数中 没有声明 同名的局部作用域标识符,那么在该函数内可以直接访问成员m。也就是说m在这样的函数中都起作用。
(2)通过表达式x.m或者X::m。这正是程序中访问对象成员的最基本方法。X::m的方式用于访问类的静态成员。
(3)通过ptr->m这样的表达式,其中ptr为指向X类的一个对象的指针。
4. 命名空间作用域
命名空间的语法形式如下:
namespace 命名空间名 { 命名空间内的各种声明(函数声明、类声明、...) }
如果需要引用其他命名空间的标识符,需要引用下面的语法:
命名空间名::标识符名
例如:
简单方法:
可见性
从标识符引用的角度来看,标识符的有效范围,即标识符的可见性。程序运行命名空间作用域到某一点,能够引用到的标识符,就是该处可见的标识符。可见性表示从内层作用域
向外层作用域“看”时能看到什么。
作用域可见性的一般规则如下:
- 标识符要声明在前,引用在后。
- 在同一作用域中,不能声明同名的标识符。
- 在没有互相包含关系的不同的作用域中声明的同名标识符,互不影响。
- 如果在两个或多个具有包含关系的作用域中声明了同名标识符,则外层标识符在内层不可见。
二、对象的生存期
静态生存期
静态生存期与程序运行周期相同,例如static、全局变量;其余全是动态生存期。
ps:定义一个静态生存期的变量后,没有初始化,该变量会被置0。
动态生存期
局部生存期对象诞生于声明点,结束于声明所在的块执行完毕之时。
例1:
若在变量 t 之前加上 static 修饰,生存期变成:调用时开始,程序最后一条语句结束后才会终止。并且,由于加上了 static 修饰,即使 foo() 被调用了两次,也只会创建销毁一次。
例2:创建的对象是匿名对象时,对象被使用完后会被立马销毁
若加上引用&,生存周期会增长,增加到最后一条语句之后才销毁。
(匿名对象引用&需要加上const)
但是这样表示后,无法直接打. 调用showtime()函数,这涉及右值引用的问题,如下解释:
右值引用
左值lvalue:locatable---定位,能取地址的值;
右值rvalue:readable---只读,不能取地址的值。
所有的匿名对象都是右值。
例:&&ref--->右值引用
三、类的静态成员
静态成员是解决同一个类的不同对象之间数据和函数共享问题的。
静态数据成员
静态数据成员具有静态生存期。一般用法:类名::成员名
特性
- 静态成员需要单独为其开空间
- 修改静态数据成员内容,所有对象的该数据都会被修改
- 可以直接访问静态数据成员变量,不经过构造函数
静态函数成员
静态成员函数可以直接访问该类的静态数据和函数成员。而访问非静态成员,必须通过对象名。
没有对象,也可以调用函数
调用方法:
this指针只存在类的非静态函数中
四、类的友元
友元关系提供了 不同类或对象的成员函数之间、类的成员函数与一般函数之间进行数据共享的机制。通俗地说,友元关系就是一个类主动声明哪些其他类或函数是它的朋友,进而给它们提供对本类的访问特许。也就是说,通过友元关系,一个普通函数或者类的成员函数可以访问封装于另外一个类中的数据。
在一个类中,可以利用关键字friend将其他函数或类声明为友元。如果友元是一般函数或类的成员函数,称为友元函数;如果友元是一个类,则称为友元类,友元类的所有成员函数都自动成为友元函数。
友元函数
友元函数是在类中用关键字friend修饰的非成员函数。虽然它不是本类的成员函数,但是在它的函数体中可以通过对象名 访问类的私有和保护成员。
例子: 在Point类中只声明了友元函数的原型,而友元函数dist 的定义在类外,却在友元函数中,通过对象名直接访问了Point类中的私有数据成员“p1.x,p1.y”,这就是友元关系的关键所在。
class Point
{
public:
Point (int x=0, int y=0) : x(x) , y(y)
{}
int getx () { return x; }
int gety () { return y; }
friend float dist (Point &p1,Point &p2);//友元函数声明
private:
int x,y;
};
float dist (Point &p1,Point &p2)
{
//友元函数实现
double x = p1.x - p2.x;
//通过对象访问私有数据成员
double y = p1.y - p2.y;
return static_cast<float> (sqrt(x*x + y*y));
}
友元类
若A类为B类的友元类,则A类的所有成员函数都是B类的友元函数,都可以访问B类的私有和保护成员。语法形式如下:
class B{ //B类的成员声明 …… friend class A; …… //声明A为B的友元类 };
注意
- 友元关系是不能被传递的。(B类是A类的友元类,C类是B类的友元类,C类和A类之间,如果没有声明,就没有任何友元关系,不能进行数据共享。)
- 友元关系是单向的。(B类是A类的友元类//B--->A,B类的成员函数就可以访问A类的私有或保护数据,但是A类不能访问B类的私有或保护数据
A--->B) - 友元关系不能被继承。(B类是A类的友元,
B类的派生类并不会自动成为A类的友元。也就是B的派生类不能继承B的友元。打个比方说,就好像别人信任你,但是不见得信任你的孩子。)
五、共享数据的保护
常对象
常对象是这样的对象:它的数据成员值在对象的整个生存期间内不能被改变。也就是说,常对象必须进行初始化,而且不能被更新。声明常对象的语法形式为:
const 类型说明符 对象名;
用const修饰的类成员
常成员函数
使用const关键字修饰的函数为常成员函数,常成员函数声明的格式如下:
类型说明符 函数名(参数表) const;
注意
- const是函数类型的一个组成部分,所以不论声明还是定义都需要加上const关键字。
- 如果一个对象是常对象,那么该常对象只能调用其常成员函数,而不能
调用其他的成员函数。 - 无论是否通过常对象调用常成员函数,目的对象都被视为常对象,因此常成员函数不能更新目的对象的数据成员,也不能调用没有用const修饰的成员函数。
- const函数可以用于重载函数的区分:常对象调用常成员函数;非const的对象一般调用不用const修饰的成员函数。
常数据成员
就像一般数据一样,类的成员数据也可以是常量,使用const说明的数据成员为常数据成员。
注意
- 常数据成员只能通过初始化列表来进行初始化
- 类成员中的静态变量和常量都应当在类定义之外加以定义,但C++标准规定了一个例外:类的静态常量如果具有整数类型或枚举类型,那么可以直接在类定义中为它指定常量值。
常引用
如果在声明引用时用const修饰,被声明的引用就是常引用。常引用所引用的对象
不能被更新。如果用常引用作形参,便不会意外地发生对实参的更改。常引用的声明形式如下:类型说明符 const &引用名
注意
- const引用后,其在参数传递时可以传递常对象。
- 需要注意的是,const不仅可以引用常对象,也可以引用普通对象,而在引用普通对象时,将这个普通对象当做常对象。
- 在实际使用时,如果不需要对实参进行更改,使用常引用可以避免意外对实参修改。
class Point
{
public:
Point (int x=0, int y=0) : x(x) , y(y)
{}
int getx () { return x; }
int gety () { return y; }
friend float dist (Point &p1,Point &p2);//友元函数声明
private:
int x,y;
};
float dist (const Point &p1,const Point &p2) //常引用做形参
{
//友元函数实现
double x = p1.x - p2.x;
//通过对象访问私有数据成员
double y = p1.y - p2.y;
return static_cast<float> (sqrt(x*x + y*y));
}
五、设计模式
单例模式
class SingelTon
{
public:
static SingelTon *getInstance()
{
if (m_p == NULL)
{
m_p = new SingelTon;//new==malloc,堆空间创建对象
}
return m_p;
}
static void destroyInstance()
{
delete m_p;//delete==free
m_p = NULL;
}
static int getCounter()
{
return counter;
}
private:
SingelTon()
{
++counter;
}
~SingelTon()
{
--counter;
}
static SingelTon *m_p;
static int counter;
};
SingelTon* SingelTon::m_p = NULL;
int SingelTon::counter = 0;
int main()
{
cout << SingelTon::getCounter() << endl;
SingelTon *p;
p = SingelTon::getInstance();
SingelTon *q;
q = SingelTon::getInstance();
cout << p << endl;
cout << q << endl;
cout << SingelTon::getCounter() << endl;
SingelTon::destroyInstance();
cout << SingelTon::getCounter() << endl;
return 0;
}