问题引入:如何知道一个类创建了多少对象?
思考:在类中添加一个普通的成员变量进行计数
class Date
{
public:
Date(int year = 1990, int month = 1, int day = 1)
:_year (year),
_month (month),
_day (day),
_count(0)
{
++_count;
}
Date(const Date&d)
{
++_count;
}
~Date()
{
--_count;
}
private:
int _year;
int _month;
int _day;
int _count;
};
void TestDate()
{
Date d1;
Date d2;
Date d3(d2);
}
int main()
{
Date d1;
Date d2;
Date d3(d2);
TestDate();
return 0;
}
调试结果
发现每一个对象的count值都是1,也就是说每一个对象的count之间没有联系,所以设置的变量不应该与对象之间有关联,应该设置一个变量是共享的,所以想到我们可以设置一个全局变量
int _count = 0;
class Date
{
public:
Date(int year = 1990, int month = 1, int day = 1)
:_year (year),
_month (month),
_day (day)
{
++_count;
}
Date(const Date&d)
{
++_count;
}
~Date()
{
--_count;
}
private:
int _year;
int _month;
int _day;
};
程序运行成功,可以求类创建了多少对象。
可是此时有一个问题?
这个程序不安全,因为任何人都可以修改这个count
思考: static所修饰的类成员,类的静态成员:静态成员变量+静态成员函数
考虑定义成 public ,不安全,类的成员一般是 private 。如果声明 private ,在类外直接调用会产生链接错误 。
然后我们可以试一下将声明放到类里,将初始化放到类外。
class Date
{
public:
Date(int year = 1990, int month = 1, int day = 1)
:_year (year),
_month (month),
_day (day)
{
++_count;
}
Date(const Date&d)
{
++_count;
}
~Date()
{
--_count;
}
private:
int _year;
int _month;
int _day;
static int _count;
};
int Date::_count = 0;
void TestDate()
{
Date d1;
Date d2;
Date d3(d2);
}
int main()
{
Date d1;
Date d2;
Date d3(d2);
TestDate();
return 0;
}
测试结果
静态成员与普通成员之间的区别:
静态成员变量不属于对象本身的内容,但可以通过打点 . 或者 sizeof 调用。
注:以 . 的方式一般是对象调用方法的方式
为什么可以这样用呢?
答案是因为它是类的特性
但是要注意的一点是sizeof()的结果是不包括静态变量。
上面这两种调用方式,第一个通过对象调用静态变量,其在底层的实现方式其实就是方式二,因为_count 是静态成员,具有类的特性,所以方式二是直接取 _count 所在位置的内容。
静态成员既然是类的成员变量,就要受到访问限定修饰符的限制
给成 private 这就与全局变量区分开了。
但在类外就不能访问,可以写一个函数 GetCount()进行访问,此时GetCount()是普通成员函数,必须通过对象调用。
比较下面这两种写法:
发现第二种写法,报错了,分析之后发现必须是在创建对象之后才能调用。但是客观来讲,它应该在开始也可以调用,此时创建对象数为零个。所以我们给 GetCount() 函数前加个static 变成静态成员函数。类比静态成员变量的特性,静态成员函数也是类的特性,可通过类来调用,不受对象的限制。
静态成员函数和普通成员函数的区别:
1. 静态成员函数没有this指针
因此在类的静态成员函数中不能访问类的非静态成员变量。
反之,则可以,因为不管静态成员函数多么特殊,它始终是类的属性,自然可以在类的其他函数中调用。
2.不能用const修饰,原因也是因为没有this 指针。
3.调用方式不一样(见下图)
两个函数在底层的实现方式不同,调用约定不同。普通成员函数,是要用this指针调用,所以调用约定是——thiscall
而静态成员函数是一般的调用约定_cdecl。