第八章 static成员、单例模式、const成员、引用类型成员
静态成员(static)
- 静态成员:被static修饰的成员变量\函数
- 访问方式:可以通过对象(对象.静态成员)、对象指针(对象指针->静态成员)、类访问(类名::静态成员)
- 静态成员变量
- 存储在数据段(全局区,类似于全局变量),整个程序运行过程中只有一份内存
- 对比全局变量,它可以设定访问权限(public、protected、private),达到局部共享的目的
- 必须初始化,必须在类外面初始化,初始化时不能带static,如果类的声明和实现分离(在实现.cpp中初始化)
-
示例1:
#include<iostream> using namespace std; class Car { public: //static int ms_count = 0;//不能直接初始化,编译报错 static int ms_count; //构造函数中直接初始化,也不行,编译报错。 /*Car() { ms_count = 0; }*/ }; //必须在类外面初始化静态成员变量 int Car::ms_count = 0; int main() { //静态成员变量的3种访问方式 Car car; car.ms_count = 10; Car *car2 = new Car(); car2->ms_count = 20; //静态成员变量独有 Car::ms_count = 30; cout << car3.ms_count << endl; getchar(); return 0; }
-
示例2:(统计车创建的次数)
#include<iostream> using namespace std; class Car { static int ms_count; public: int getCount() { return ms_count; } Car() { ms_count ++; } ~Car() { ms_count--; } }; //必须在类外面初始化静态成员变量 int Car::ms_count = 0; int main() { Car car; Car *car2 = new Car(); Car car3; cout << car3.getCount() << endl; getchar(); return 0; }
-
- 静态成员函数
- 从上面示例代码可以看出,如果要访问私有静态成员变量,必须通过对象去访问
car3.getCount()
- 但是静态成员变量存放在全局区,并不依赖于类,那么可不可以直接通过类来访问静态成员变量呢?
-
一般情况下,私有静态成员变量要通过静态成员函数来访问
#include<iostream> using namespace std; class Car { static int ms_count; public: //静态成员函数 static int getCount() { return ms_count; } Car() { ms_count ++; } ~Car() { ms_count--; } }; //必须在类外面初始化静态成员变量 int Car::ms_count = 0; int main() { Car car; Car *car2 = new Car(); Car car3; cout << car3.getCount() << endl; //静态成员函数getCount可以直接通过类名来访问 cout << Car :: getCount() << endl; getchar(); return 0; }
- 内部不能使用this指针(this指针只能用在非静态成员函数内部)
- 不能是虚函数(虚函数只能是非静态成员函数)
- 因为虚函数是用来实现多态的
- 而且虚函数一定是通过对象来调用的
- 而静态成员函数可以不通过指针调用,所以错误。
- 内部不能访问非静态成员变量\函数,只能访问静态成员变量\函数
- 非静态成员函数内部可以访问静态成员变量\函数
- 构造函数、析构函数不能是静态
- 当声明和实现分离时,实现部分不能带static,只能放在声明中。
- 继承静态成员变量:静态成员变量内存中只有一份,继承仅仅是继承非静态的,因此,静态成员变量不在对象内部,不占用对象内存。
- 静态成员函数就相当于OC的类方法,静态成员函数不能访问非静态成员变量,OC中的类方法内部也不能访问成员变量。
- 示例1
//Person声明与实现分开实现 class Person { public: static int ms_age; }; class Student : public Person { }; int Person:: ms_age = 10; int main() { Person::ms_age = 10; Student::ms_age = 20; //打印结果20,静态成员变量内存中只有一份,继承仅仅是继承非静态的 cout << Person::ms_age << endl; //只占用一个字节,因为ms_age在全局区,并不在对象内部. 空对象占用一个字节. cout << sizeof(Person) << endl; }
- 示例2:
#include<iostream> using namespace std; //Person声明与实现分开实现 class Person { //类的声明,可以加static static void test(); }; //实现不能加static // static void Person:: test() { void Person:: test() { } class Car { public: static int ms_count; int m_price; //普通成员变量在对象中,所以创建了对象才可以访问他,this 就是这个对象的指针 void test() { this->m_price = 10; } //静态成员函数 //静态成员变量可以通过类名来访问Car::test2();此时没有对象,因此这时用this访问肯定错误 static void test2() { //不能用this //this->m_price = 10; //不能访问非静态成员变量,跟上面一个意思 //m_price = 10; //不能调用非静态成员函数 //test(); //只能访问静态成员变量\函数 ms_count = 10; } //非静态成员函数内部可以访问静态成员变量\函数 void run() { ms_count = 20; m_price = 10; test(); test2(); } //不能修饰虚函数 //static virtual void test3(){} //构造函数、析构函数不能是静态 /*static Car() { } static ~Car() { }*/ }; //必须在类外面初始化静态成员变量 int Car::ms_count = 0; int main() { Car car; Car::test2(); getchar(); return 0; }
- 从上面示例代码可以看出,如果要访问私有静态成员变量,必须通过对象去访问
静态成员经典应用 – 单例模式
- 单例:某些类的实例对象永远只有一个
- 步骤
- 把构造函数私有化,让别人无法创建对象
- 定义一个私有的静态成员变量指针(指针值占用4个字节,如果是个对象,那么对象大了就非常占用内存),用于指向单例对象
- 提供的一个公共的静态的返回单例对象的成员函数
class Rocket { public: //3. static Rocket *shateRocket() { //严格来讲,这段代码需要考虑线程安全问题。因此这里面需要加线程安全 if (ms_instance == NULL) { ms_instance = new Rocket(); } return ms_instance; } static void deleteRocket() { if (ms_instance == NULL) return; //1.也要注意线程安全问题 //2. delete后,一定要清空NULL,否则出现野指针错误.delete仅仅是释放内存,但是ms_instance仍然指向原来的内存 //3. 下次创建的时候,ms_instance不为NULL,所以还是返回这个内存,但此时这块内存中存储的已经是其他数据的,野指针错误 delete ms_instance; ms_instance = NULL; } private: //1 Rocket() {} //2 static Rocket *ms_instance; }; //静态成员初始化 Rocket * Rocket::ms_instance = NULL; int main() { Rocket *p1 = Rocket::shateRocket(); Rocket *p2 = Rocket::shateRocket(); Rocket *p3 = Rocket::shateRocket(); //内存地址一样 cout << p1 << endl; cout << p2 << endl; cout << p3 << endl; getchar(); return 0; }
const成员
- const成员:被const修饰的成员变量、非静态成员函数
- const成员变量
- 必须初始化(类内部初始化)
- 可以在声明的时候直接初始化赋值
- 非static的const成员变量还可以在初始化列表中初始化
class Car { public: int m_price; // const修饰成员变量必须被初始化,否则创建对象报错 //可以在声明的时候直接初始化赋值 // const int m_wheelCount = 4; //也可以在初始化列表中初始化 const int m_wheelCount; Car() : m_wheelCount(4) {} };
- 必须初始化(类内部初始化)
- const成员函数(非静态)
- const关键字写在参数列表后面,函数的声明和实现都必须带const
- 内部不能修改当前对象的非static成员变量,但是可以访问
- 内部只能调用const成员函数、static成员函数
- 非const成员函数可以调用const成员函数
- const成员函数和非const成员函数构成重载
- 非const对象(指针)优先调用非const成员函数
- const对象(指针)只能调用const成员函数、static成员函数(注意理解,为何有些类一定要实现const类型的成员函数)
- 比如要实现:
"123".length()
,就必须实现const类型的length()成员函数,因为“123”是const类型
class Car { public: // const修饰成员变量必须被初始化,否则创建对象报错 //可以在声明的时候直接初始化赋值 const int m_wheelCount = 4; int m_price; //可以在初始化列表中初始化 Car() : m_wheelCount(4) {} //const修饰成员函数 //不能是静态的成员函数 //static void run() const void run() const;//声明 void test2() { this->m_price = 20; //非const成员函数可以调用const成员函数 test3(); } //const成员函数 void test3 () const { cout << "test3-const" << endl; } //const 非const构成重载 void test3() { cout << "test3" << endl; } //静态成员函数 static void test4() {} }; //实现,声明实现都必须加const void Car::run() const { //内部不能修改当前对象的非static成员变量 //this->m_price = 20; //但是可以访问 this->m_price; //内部只能调用const成员函数、static成员函数 //不可以 //test2(); //可以 test3(); //可以 test4(); } int main() { //非const对象(指针)优先调用非const成员函数(如果test3没有实现,那么就会调用test3-const) //调用test3 Car car1; car1.test3(); //const对象(指针)只能调用const成员函数、static成员函数(如果test3-const没有实现,那么就会报错) //调用test3-const const Car car2; car2.test3(); getchar(); return 0; }
- 比如要实现:
- const关键字写在参数列表后面,函数的声明和实现都必须带const
引用类型成员
- 引用类型成员变量必须初始化(不考虑static情况)
- 在声明的时候直接初始化
- 通过初始化列表初始化
class Person { int m_age; //在声明的时候直接初始化 int &m_price = m_age; public: //通过初始化列表初始化 Person(int &price):m_price(price){} };