第八章 static成员、单例模式、const成员、引用类型成员

静态成员(static)

  1. 静态成员:被static修饰的成员变量\函数
    1. 访问方式:可以通过对象(对象.静态成员)、对象指针(对象指针->静态成员)、类访问(类名::静态成员)
  2. 静态成员变量
    1. 存储在数据段(全局区,类似于全局变量),整个程序运行过程中只有一份内存
    2. 对比全局变量,它可以设定访问权限(public、protected、private),达到局部共享的目的
    3. 必须初始化,必须在类外面初始化,初始化时不能带static,如果类的声明和实现分离(在实现.cpp中初始化)
      1. 示例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. 示例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;
         }
        
  3. 静态成员函数
    1. 从上面示例代码可以看出,如果要访问私有静态成员变量,必须通过对象去访问car3.getCount()
    2. 但是静态成员变量存放在全局区,并不依赖于类,那么可不可以直接通过来访问静态成员变量呢?
    3. 一般情况下,私有静态成员变量要通过静态成员函数来访问

       #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;
       }
      
    4. 内部不能使用this指针(this指针只能用在非静态成员函数内部)
    5. 不能是虚函数(虚函数只能是非静态成员函数)
      1. 因为虚函数是用来实现多态的
      2. 而且虚函数一定是通过对象来调用的
      3. 而静态成员函数可以不通过指针调用,所以错误。
    6. 内部不能访问非静态成员变量\函数,只能访问静态成员变量\函数
    7. 非静态成员函数内部可以访问静态成员变量\函数
    8. 构造函数、析构函数不能是静态
    9. 当声明和实现分离时,实现部分不能带static,只能放在声明中。
    10. 继承静态成员变量:静态成员变量内存中只有一份,继承仅仅是继承非静态的,因此,静态成员变量不在对象内部,不占用对象内存。
    11. 静态成员函数就相当于OC的类方法,静态成员函数不能访问非静态成员变量,OC中的类方法内部也不能访问成员变量。
      1. 示例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;
      	}
      
      1. 示例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;
      }
      

静态成员经典应用 – 单例模式

  1. 单例:某些类的实例对象永远只有一个
  2. 步骤
    1. 把构造函数私有化,让别人无法创建对象
    2. 定义一个私有的静态成员变量指针(指针值占用4个字节,如果是个对象,那么对象大了就非常占用内存),用于指向单例对象
    3. 提供的一个公共的静态的返回单例对象的成员函数
     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成员

  1. const成员:被const修饰的成员变量、非静态成员函数
  2. const成员变量
    1. 必须初始化(类内部初始化)
      1. 可以在声明的时候直接初始化赋值
      2. 非static的const成员变量还可以在初始化列表中初始化
       class Car {
       public:
       int m_price;
       // const修饰成员变量必须被初始化,否则创建对象报错
       //可以在声明的时候直接初始化赋值
       //	const int m_wheelCount = 4;
       //也可以在初始化列表中初始化
       const int m_wheelCount;
       Car() : m_wheelCount(4) {}
       };
      
  3. const成员函数(非静态)
    1. const关键字写在参数列表后面,函数的声明和实现都必须带const
      1. 内部不能修改当前对象的非static成员变量,但是可以访问
      2. 内部只能调用const成员函数、static成员函数
      3. 非const成员函数可以调用const成员函数
    2. const成员函数和非const成员函数构成重载
      1. 非const对象(指针)优先调用非const成员函数
    3. const对象(指针)只能调用const成员函数、static成员函数(注意理解,为何有些类一定要实现const类型的成员函数)
      1. 比如要实现:"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;
       }
      

引用类型成员

  1. 引用类型成员变量必须初始化(不考虑static情况)
    1. 在声明的时候直接初始化
    2. 通过初始化列表初始化
     class Person {
     	int m_age;
     	//在声明的时候直接初始化
     	int &m_price = m_age;
     public:
     	//通过初始化列表初始化
     	Person(int &price):m_price(price){}
     };
    
Table of Contents