在一个类中,如果讲一个成员变量声明为static
,那么这个成员就成为静态成员变量。与普通的成员不同,静态成员变量无论创建了多少个对象,都只有一个静态数据的拷贝。静态成员包括了静态成员变量和静态成员函数。
静态成员变量是属于某一个类的,所有成员所共享。因此如果不加限制的话我们可以通过对象名或者是类名对静态的成员进行调用。静态成员变量是在编译阶段就分配了内存,因此早于对象的创建。静态成员变量必须声明在类中,在类外进行定义。在给类对象分配空间的时候并不包括静态成员变量的空间。
静态成员函数是将类中的成员函数声明为static
类型。成静态成员函数与静态成员变量一样,在类没有创建之前就可以通过类名来调用。静态成员函数主要是为了访问静态变量的,但是不能访问类中的普通成员函数。
上面的静态成员函数和静态成员变量也有访问权限。私有的权限在类外是访问不到的。关于静态成员的示例代码如下所示:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
class Person
{
public:
// 1. 静态成员变量
// 静态成员变量:编译阶段就分配了内存
// 类内声明、类外初始化
// 静态成员变量 所有对象都共享同一份数据
static int A;
// 2. 静态成员函数
// 所有对象都共享一个func函数
static void func()
{
A = 20; // 静态成员函数能访问静态成员变量
//C = 20; // 静态成员函数不能访问静态成员变量
cout << "func 调用" << endl;
}
int C;
private:
static int B; // 私有静态成员变量
static void func2()
{
}
};
int Person::A = 0;
int Person::B = 0;
void test01()
{
// 1. 通过对象进行访问
Person p1;
cout << p1.A << endl;
Person p2;
p2.A = 100;
cout << p1.A << endl;
// 2. 通过类名进行访问
cout << Person::A << endl;
// 静态成员变量,也是有访问权限的,私有权限类外访问不到
//cout << Person::B << endl;
}
void test02()
{
// 通过对象
Person p1;
p1.func();
// 通过类名
Person::func();
//Person::func2(); // 静态成员函数也是有访问权限的
}
int main()
{
test01();
test02();
system("pause");
return 0;
}
单例设计模式是一种常用的软件设计模式。单例模式内部只有一个对象。那么怎么样实现单例模式呢?首先我们需要避免用户去创建对象,因此我们需要将一个类的构造函数进行私有化。但是这样就无法对对象的成员方法以及构造函数进行调用,为了能够创建一个类,因此我们可以定义一个静态成员函数去返回一个类的对象。为了对象始终是一个,所有我们应该定义一个类对象的指针去初始化这个对象,这个类指针需要是静态的。这样我们也创建好一个单例,作为外部共享的唯一对象示例。由于静态变量在编译阶段就分配了内存,所以在单例设计模式中,构造函数要比main
函数先执行。
例如我们有一个主席的位置,变动的只是主席的人是谁,而不是主席这个位置。因此我们可以设计一个主席类的单例。代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
// 主席类
class ChairMan
{
public:
static ChairMan *getInstance()
{
return singleMan;
}
private:
// 将构造函数私有化,不可以创建多个对象
ChairMan(){}
ChairMan(const ChairMan &){}
// 将主席指针私有化,对外提供只读接口
static ChairMan *singleMan; // 类内声明 类外初始化
};
ChairMan *ChairMan::singleMan = new ChairMan();
int main()
{
//ChairMan c1;
//ChairMan c2;
//ChairMan *c3 = new ChairMan;
//ChairMan *c1 = ChairMan::singleMan;
//ChairMan *c2 = ChairMan::singleMan;
ChairMan *c1 = ChairMan::getInstance();
ChairMan *c2 = ChairMan::getInstance();
//ChairMan *c3 = new ChairMan(*c2);
if (c1 == c2)
{
cout << "c1 == c2" << endl;
}
else
{
cout << "c1 != c2" << endl;
}
//if (c3 == c2)
//{
// cout << "c3 == c2" << endl;
//}
//else
//{
// cout << "c3 != c2" << endl;
//}
system("pause");
return 0;
}
在公司中,有些公司可能比较穷,就只有一台打印机。而每个员工使用打印服务就只能对这台打印机进行操作。因此打印机就是一个单例。对此我们可以利用单例设计模式设计一个打印机的类,示例代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
class Printer
{
public:
static Printer *getInstance()
{
return printer;
}
void printText(string text)
{
cout << text << endl;
print_count++;
}
int getPrintCount()
{
return print_count;
}
private:
Printer()
{
print_count = 0;
cout << "打印机构造函数调用" << endl;
}
Printer(const Printer &){}
static Printer *printer;
int print_count;
};
Printer *Printer::printer = new Printer;
int main()
{
cout << "main 函数调用" << endl;
Printer *p1 = Printer::getInstance();
p1->printText("p1 打印第一次");
p1->printText("p1 打印第二次");
p1->printText("p1 打印第三次");
cout << "打印机使用次数: " << p1->getPrintCount() << "次" << endl;
Printer *p2 = Printer::getInstance();
p2->printText("p2 打印第一次");
p2->printText("p2 打印第二次");
p2->printText("p2 打印第三次");
p2->printText("p2 打印第四次");
cout << "打印机使用次数: " << p1->getPrintCount() << "次" << endl;
system("pause");
return 0;
}
在C语言中,成员变量和函数是分开来声明的,成员变量与函数的关联性通过参数的传递而实现,处理的是共同的外部数据,编程语言并不支持数据和函数之间的关联性。
在C++中实现了封装,将成员变量和成员函数进行封装在了一起。虽然封装在了一起,但是数据和处理数据的函数却是分离的,分开进行存储的。C++的非静态成员变量是直接包含在类对象之中的。我们对一个对象用sizeof
运算符得到的结果就是在类中非静态成员变量所占的内存空间。但是有个例外的是空对象使用sizeof
运算的话结果会是1
。因为每个对象在内存上都有独一无二的地址,因此编译器给空对象分配1个字节的空间大小。此外每个非内联函数只会有一份函数的实例。
下面给出一个示例代码来证明C++中类对象的变量和函数是分开存储的。
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
class Person
{
int m_A; // 只有非静态成员变量属于类对象上
void func() // 成员函数不属于类对象上
{
}
static int m_B; // 静态成员变量不属于类对象上
static void func2() // 静态成员函数不属于类对象上
{
}
double m_C;
};
int Person::m_B = 0;
int main()
{
// 空类的sizeof结果是1 原因是每个对象在内存上有独一无二的地址,因此给空对象分配1个字节的空间
Person p1;
// 空对象大小为1
cout << "sizeof p1 = " << sizeof p1 << endl;
system("pause");
return 0;
}
既然上面我们已经知道了非内联成员函数只有一份函数实例,那么对象是如何区分究竟是哪个函数调用了这个成员函数的示例呢?其实就是通过了this
指针。C++中调用成员函数的时候都会隐含地传递了一个this
指针。this
指针指向被调用成员函数所属的对象。
this
指针是C++实现封装的一种机制,它将对象和该对象调用的函数连接在了一起。this
指针永远指向当前对象。
那么this
指针有什么用处呢?首先在形式参数和成员变量同名的时候,可以用this
指针来区分。同样在类的非静态成员函数中返回对象本身也可以使用return *this;
。
关于this
指针使用的示例代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
class Person
{
public:
Person(int age)
{
// 用途1: 解决名称冲突
this->age = age;
}
// this指针隐式加在每个成员函数中
bool compareAge(Person &p)
{
return this->age == p.age;
}
Person& personAddPerson(Person &p)
{
this->age += p.age;
return *this; // *this就是本体
}
int age;
};
int main()
{
// this 指针指向被调用的成员函数所属的对象
Person p1(10);
cout << "p1的年龄为: " << p1.age << endl;
Person p2(10);
bool ret = p1.compareAge(p1);
if (ret)
{
cout << "p1与p2的年龄相等" << endl;
}
p1.personAddPerson(p2).personAddPerson(p2).personAddPerson(p2).personAddPerson(p2); // 链式编程
cout << "p1的年龄为: " << p1.age << endl;
system("pause");
return 0;
}
空指针也可以进行访问成员函数的,但是有时候空指针访问成员函数也会报错。若成员函数中没有使用成员变量,则不会出现错误,若成员函数中使用了成员变量,则会报错。所以有时候有些程序员在写成员函数的时候会先判断一下this
指针是否为空。但是其实并没有这个必要。关于空指针访问成员函数的示例代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
class Person
{
public:
void showClass()
{
cout << "The class name is Person" << endl;
}
void showAge()
{
if (this == NULL)
{
return;
}
cout << "age = " << this->age << endl;
}
int age;
};
int main()
{
Person *p = NULL;
p->showClass();
p->showAge();
system("pause");
return 0;
}
当用const
去修饰成员函数时,该函数就是一个常函数。常函数中的const
实际上是修饰的是this
指针指向的那块内存区域。也就是该对象的成员变量不能再被修改。但是有些情况下我们实在想要去进行修改某些个别的变量,此时可以使用关键字mutable
对需要修改的变量进行修饰。
而常对象是用const修饰的对象实例,该成员的成员变量不能被修改,如果要修改也是在需要修改的成员变量的前面添加关键字mutable
。
关于常函数和常对象的示例代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
class Person
{
public:
Person(int age)
{
this->age = age;
}
// 常函数:修饰成员函数中的this指针,让指针指向的值不可以修改
void showPerson() const
{
//this->age = 100;
this->A = 20;
// this 指针的本质: Person * const this
// 常函数的this指针: const Person * const this
// this = NULL; 指针的指向不可以修改,而指针指向的值可以改
cout << "Person age = " << this->age << endl;
}
void func()
{
age = 10;
cout << "func 调用" << endl;
}
int age;
mutable int A; // 常函数中或者常对象中,有些特殊属性依然想要修改,加关键字mutable
};
int main()
{
// 常对象
const Person p1(10);
//p1.age = 10;
p1.A = 10;
p1.showPerson();
//p1.func(); // 常对象只能调用常函数
system("pause");
return 0;
}
类的私有成员无法在类外进行访问,就像你家的卧室一样不能能每一个人都能够进去。当你遇到你的好闺蜜或者好基友的时候,你或许会让他们进入你的卧室。在C++中,为了实现这样的功能,我们定义了友元。友元是一种特权,可以让一个类、一个函数等拥有访问另一个类的私有成员的特权。
友元的关键字是friend
,但是该关键字只能出现在声明处。全局函数、类、成员函数都可以声明为友元。友元函数不是类的成员,因此不带this
指针。
下面我们看一个关于全局函数作为友元函数的示例代码:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
class Building
{
// 利用friend关键字让全局函数goodGay作为本类好朋友,可以访问私有成员
friend void goodGay(Building *);
public:
Building()
{
this->sittingRoom = "客室";
this->bedRoom = "卧室";
}
public:
string sittingRoom;
private:
string bedRoom;
};
// 好基友全局函数可以访问Building的私有属性
void goodGay(Building *building)
{
cout << "好基友正在访问: " << building->sittingRoom << endl;
cout << "好基友正在访问: " << building->bedRoom << endl;
}
int main()
{
Building building;
goodGay(&building);
system("pause");
return 0;
}
以上面所提例子为例,改成类做友元类,代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
class Building;
class GoodGay
{
public:
GoodGay();
void visit();
Building *pBuilding;
};
class Building
{
// 让GoodGay类作为Building的好朋友,可以访问私有成员
friend class GoodGay;
public:
Building();
string sittingRoom;
private:
string bedRoom;
};
Building::Building()
{
this->sittingRoom = "客厅";
this->bedRoom = "卧室";
}
GoodGay::GoodGay()
{
this->pBuilding = new Building;
}
void GoodGay::visit()
{
cout << "好基友正在访问: " << this->pBuilding->sittingRoom << endl;
cout << "好基友正在访问: " << this->pBuilding->bedRoom << endl;
}
int main()
{
GoodGay gg;
gg.visit();
system("pause");
return 0;
}
我们继续将上面的例子改为成员函数作为友元函数的例子,示例代码如下:
#define _CRT_SECURE_NO_WARNINGS
#include
using namespace std;
#include
class Building;
class GoodGay
{
public:
GoodGay();
void visit1(); // 可以访问Building中的私有
void visit2(); // 不可以访问Building中的私有
Building *pBuilding;
};
class Building
{
// 让GoodGay中的visit1成员函数作为友元
friend void GoodGay::visit1();
public:
Building();
string sittingRoom;
private:
string bedRoom;
};
GoodGay::GoodGay()
{
this->pBuilding = new Building;
}
Building::Building()
{
this->bedRoom = "卧室";
this->sittingRoom = "客厅";
}
void GoodGay::visit1()
{
cout << "好基友正在访问: " << this->pBuilding->sittingRoom << endl;
cout << "好基友正在访问: " << this->pBuilding->bedRoom << endl;
}
void GoodGay::visit2()
{
cout << "好基友正在访问: " << this->pBuilding->sittingRoom << endl;
//cout << "好基友正在访问: " << this->pBuilding->bedRoom << endl;
}
int main()
{
GoodGay gg;
gg.visit1();
system("pause");
return 0;
}