对象的使用(一)
static 成员
- 对于特定类型的全体对象而言,有时候可能需要访问一个全局的变量,比如说统计某种类型对象已创建的数量;
- 如果我们用全局变量会破坏数据的封装,一般的用户代码都可以修改这个全局变量,这时候我们可以用类的静态成员来解决这个问题;
- 非 static 数据成员存在于类类型的每个对象中,static 数据成员独立该类的任意对象存在,它是与类关联的对象,不与类对象关联。与所有的类对象是共享的
class CountedObject { public: CountedObject(); ~CountedObject();
static int count_; };
int CountedObject::count_ = 100;
|
static 成员的优点
- static 成员的名字是在类的作用域中,因此可以避免与其他类成员或全局对象名字冲突;
- 可以实施封装,static 成员可以是私有的,而全局对象不可以;如果 static 成员是私有的,意味着外部的代码不能访问该成员,需要提供公有的接口来访问。
- 阅读顺序可以看出 static 成员与某个类相关联,这种可见性可以清晰地反映程序员的意图。
class CountedObject { public: CountedObject(); ~CountedObject(); public: static int GetCount(); private: static int count_; };
int CountedObject::GetCount() { return count_; }
|
- static 成员的定义
- static 成员需要在类定义体外进行初始化与定义
- 特殊的整型 static const 成员
- 整型 static const 成员可以在类定义体中初始化,
- 注意不能够多次初始化
static 成员函数
- static 成员函数没有 this 指针
- 非静态成员函数可以访问静态成员
- 静态成员函数不可以访问非静态成员,静态成员函数不能调用非静态成员函数
- 静态成员函数不能访问非静态成员,因为静态成员函数没有 this 指针
- 访问静态成员函数可以
Test::x_
来访问,推荐这种方式,除此之外,还可以通过点的方式来访问t.x_
,不推荐
类/对象大小计算
- 类大小计算遵循前面学过的结构体对齐原则
- 类的大小与数据成员有关,与成员函数无关
- 类的大小与静态数据成员无关
- 虚函数对类的大小的影响
- 虚继承对类的大小的影响
对象的使用(二)
static 成员函数的用法
#include <iostream>
using namespace std;
class Date { public: Date(int year) : year_(year) {
} static bool IsLeapYear(int year) { return (year % 4 == 0 && year % 100 != 0) || (year % 400 == 0); }
bool IsLeapYear() { return (year_ % 4 == 0 && year_ % 100 != 0) || (year_ % 400 == 0); }
private: int year_; };
int main(void) { Date d(2012); cout << d.IsLeapYear() << endl;
cout << Date::IsLeapYear(2010) << endl; return 0; }
|
四种对象作用域与生存期
- 栈对象
- 堆对象
- 全局对象、静态全局对象
- 全局对象的构造先于 main 函数
- 已初始化的全局变量或静态全局对象存储于 .data 段中
- 未初始化的全局变量或静态全局对象存储于 .bss 段中
- 静态局部对象
- 已初始化的静态局部变量存储于 .data 段中
- 未初始化的静态局部变量存储于 .bss 段中
- 作用域与生存期不总是等同的
Test t; Test *t3 = new Test; delete t3;
|
#include <iostream> using namespace std; class Test { public: Test(int n) : n_(n) { cout << "Test..." << n_ << endl; } ~Test() { cout << "~Test..." << n_ << endl; } private: int n_;
}; int n; int n2 = 101; Test g(100); static Test g2(200); int main(void) { cout << "Enetring main..." << endl; Test t(10); { Test t(20); } { Test *t3 = new Test(30); delete t3; }
{ static int n3; static int n4 = 102; static Test t4(333); } cout << "Exiting main..." << endl; return 0; }
|
static 用法总结
- 用于函数内部修饰变量,即函数内的静态变量,这种变量的生存期长于该函数,使得函数具有一定的“状态”,使用静态变量的函数一般是不可以重入的,也不是线程安全的,比如
strtok(3)
- 用在文件级别(函数体之外),修饰变量或函数,表示该变量或函数只在本文件可见,其他文件看不到也访问不到该变量或函数。专业的说法叫“具有 internal linkage” (简言之,不暴露给别的 translation unit)。C 语言的这两种用法很明确,一般也不容易混淆。由于 C++ 引入了类,在保持与 C 语言兼容的同时,static 关键字又有了两种新用法:
- 用于修饰类的数据成员,即所谓的“静态成员”。这种数据成员的生存期大于 class 的对象(实例/instance)。静态数据成员是每个 class 有一份,普通数据成员是每个 instance 有一份。
- 用于修饰 class 的成员函数,即所谓的“静态成员函数”。这种成员函数只能访问静态成员和其他静态成员函数,不能访问非静态成员和非静态成员函数(没有 this 指针)。
对象的使用(三)
static 与单例模式
单例模式:是一种设计模式,保证一个类只有一个实例,并提供一个全局访问点,禁止拷贝
#include <iostream> using namespace std;
class Singleton{ public: static Singleton* GetInstance() { if (instance_ == NULL) { instance_ = new Singleton; } return instance_; }
private: Singleton(const Singleton& other); Singleton& operator =(const Singleton& other); Singleton() { cout << "Singleton..." << endl;
} ~Singleton() { cout << "~Singleton..." << endl; }
static Singleton* instance_;
}; Singleton* Singleton::instance_;
int main(void) {
Singleton* s1 = Singleton::GetInstance(); Singleton* s2 = Singleton::GetInstance();
return 0; }
|
#include <iostream> using namespace std;
class Singleton{ public: static Singleton* GetInstance() { if (instance_ == NULL) { instance_ = new Singleton; } return instance_; } ~Singleton() { cout << "~Singleton..." << endl; }
class Garbo { public: ~Garbo() { if(Singleton::instance_ != NULL) { delete instance_; } } };
private: Singleton(const Singleton& other); Singleton& operator =(const Singleton& other); Singleton() { cout << "Singleton..." << endl;
} static Singleton* instance_; static Garbo garbo_;
};
Singleton::Garbo Singleton::garbo_; Singleton* Singleton::instance_;
int main(void) {
Singleton* s1 = Singleton::GetInstance(); Singleton* s2 = Singleton::GetInstance();
return 0; }
|
#include <iostream> using namespace std;
class Singleton{ public: static Singleton& GetInstance() { static Singleton instance; return instance; } ~Singleton() { cout << "~Singleton..." << endl; }
private: Singleton(const Singleton& other); Singleton& operator =(const Singleton& other); Singleton() { cout << "Singleton..." << endl;
}
};
int main(void) {
Singleton& s1 = Singleton::GetInstance(); Singleton& s2 = Singleton::GetInstance();
return 0; }
|
对象的使用(四)
const 成员函数
- const 成员函数不会修改对象的状态
- const 成员函数只能访问数据成员的值,而不能修改它
#include <iostream> using namespace std;
class Test { public: Test(int x) : x_(x) {} int GetX() const { cout << "const GetX..." << endl;
return x_; }
int GetX() { cout << "GetX..." << endl; return x_; } private: int x_; }; int main(int argc, char *argv[]) { Test t(10); t.GetX(); cout << "Hello World!" << endl; return 0; }
|
const 对象
- 如果把一个对象指定为 const,就是告诉编译器不要修改它
- const 对象的定义:
const 类名 对象名(参数表)
- const 对象不能调用非 const 成员函数,只能够调用 const 成员函数
const Test t(10); t.GetX();
|
mutable
- 用 mutable 修饰的数据成员即使在 const 对象或在 const 成员函数中都可以修改。
mutable int outputTimes_;
const 用法总结
const int n= 100;
// 定义常量,必须初始化;const Test t(10);
// 定义常量对象,必须初始化;const int& ref = n;
// const 引用可以引用 const 常量,普通引用不能引用 const 常量;int& ref = n; //error
;- const 与指针
const int* p;
// const 出现在*左边,表示 *p
是常量,const 修饰的是 p指针指向的对象(*p = 200; error);int* const p2;
// const 出现在*右边,表示 p2 是常量,(p2 = &n; error);const int* const p3 = &n3
// *p3是常量,p3也是常量;
- 在类中,如果有 const 成员,const 成员的初始化只能在构造函数初始化列表中进行;
- const 修饰成员函数,表示该成员函数不能修改对象状态,也就是它只能访问数据成员,但是不能修改数据成员。
一个实例看数据抽象与封装
#include <iostream> using namespace std; class Stack { struct Link { int data_; Link* next_; Link(int data, Link* next) : data_(data), next_(next) {} }; public: Stack() : head_(0), size_(0) {} ~Stack() { Link* tmp; while (head_) { tmp = head_; head_ = head_->next_; delete tmp; } }
void Push(const int data) { Link* node = new Link(data, head_); head_ = node; ++size_; } bool Empty() { return (size_ == 0); }
bool Pop(int& data) { if(Empty()) { return false; } Link* tmp = head_; data = head_->data_; head_ = head_->next_; delete tmp; --size_; }
private: Link* head_; int size_;
};
int main(void) { Stack stack; int i; for (i = 0; i < 6; i++) { stack.Push(i); }
while (!stack.Empty()) { stack.Pop(i); cout << i << " "; } return 0; }
|