Day04
一、静态成员
1.在类定义中,成员变量和成员函数都可以用static声明为静态的,称为静态成员。无论该类实例了多少个对象,静态成员只有一个拷贝,这个拷贝被所有属于这个类的对象共享。
2.静态成员变量、静态成员函数的访问也是有权限的
3.静态的成员函数被所有对象共享
4.静态成员函数可以访问静态成员变量,但不能访问非静态的成员变量
5.均可通过两种方式访问。一种是实例化对象,一种是用过类名。
二、单例模式
单例是设计模式里面的一种,全局有且只有一个类的static实例,在程序任何地方都能够调用到。(某些东西只有一个,单独且唯一的一个例子)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
// 单例模式
class ChairMan
{
public:
static ChairMan *getInstance() //由于构造函数的私有化,通过此函数提供一个外界访问的接口函数
{
return theSingalMan;
}
private:
ChairMan(){}; // 构造函数私有化,不可以创建多个对象
ChairMan(const ChairMan &){}; // 拷贝构造函数私有化,防止拷贝,实现真的有且只有
private: // 只提供对外只读接口
static ChairMan *theSingalMan; // 共享访问,类内声明,类外初始化
// 定义一个唯一指向实例的静态指针,并且是私有的。
};
ChairMan *ChairMan ::theSingalMan = new ChairMan;
int main()
{
// ChairMan *c1 = ChairMan::theSingalMan; //有缺陷:提供了可读可写的权限
// ChairMan *c2 = ChairMan::theSingalMan;
// if (c1 == c2)
// {
// cout << "yes" << endl;
// }
// else
// {
// cout << "NO" << endl;
// }
ChairMan *c1 = ChairMan::getInstance();
ChairMan *c2 = ChairMan::getInstance();
if (c1 == c2)
{
cout << "yes" << endl;
}
else
{
cout << "NO" << endl;
}
system("pause");
return EXIT_SUCCESS;
}
设计步骤:
(1)声明一个类,此类满足全局有且只有的条件
(2)私有化构造函数和拷贝构造函数
(3)定义一个指向实例的静态指针,并且设置为私有。类内定义,类外声明
(4)公有化一个外界访问该实例的静态接口函数
(5)通过类名调用接口函数实现访问实例
三、Static的修饰作用
1.因为静态数据成员不属于类的任何一个对象,所以它不是在创建类的对象的时候被定义和初始化的。
2.static修饰类内数据成员时:
(1)表明这个数据成员独立于所有对象之外,所以只能在外部定义和初始化每个静态(数据)成员。并且一个静态数据成员只能定义一次。 (2)static成员变量的内存在所有对象之外,保存在全局数据区,所以它的声明周期等同于整个程序的周期,static数据成员被定义后就一直存在于程序的整个生命周期中。
3.static修饰成员函数时:
(1)带有static关键字的类成员函数能访问的变量只有、全局变量、参数、类内static数据成员
(2)共享访问,类内声明,类外初始化
(3)静态函数成员不持有this指针,不能声明为const(常量成员函数)
四、函数的构造和析构
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
class Person
{
public:
Person()
{
cout << "person的构造函数调用" << endl; // 构造时自动调用
};
~Person() //此处需要增加一个~符号,函数名和构造函数名是一致的
{
cout << "析构函数调用" << endl; // 生命周期结束后自动调用
}
};
int main()
{
Person p;
system("pause");
return EXIT_SUCCESS;
}
五、构造函数分类
1.有参和无参构造
class Person
{
public:
Person()
{
cout << "默认构造函数" << endl;
}
Person(int a)
{
cout << "有参构造" << endl;
}
};
2.普通构造函数和拷贝构造函数
class Person
{
public:
Person()
{
cout << "默认构造函数" << endl;
}
Person(int a)
{
age = a;
cout << "有参构造" << endl;
}
Person (const Person &p)
{
age = p.age;
}
int age;
};
3.调用方法
Person p(10); //括号法
Person p = Person(100); //显示法
Person p3 = 10; //=Person p3 = Person(10); 隐式法
注意:
(1)括号法不能调用无参构造
(2)不要用拷贝狗杂初始化匿名对象
六、拷贝函数调用时机
1.用已将创建好的对象来初始化新的对象
void test()
{
Person p1(18);
Person p2 = Person(p1);
cout << "p2的年龄=" << p2.age << endl;
}
2.值传递的方式给函数参数传值
void doWork(Person p)
{
}
void test2()
{
Person p1(100);
doWork(p1);
}
3.以值方式,返回局部对象(优化后,不会调用拷贝构造)
Person doWork2()
{
Person p;
return p;
}
void test3()
{
Person p = doWork2();
}
七、构造函数的调用规则
1.编译器会给一个类至少添加三个函数:默认构造、析构函数、拷贝构造
2.如果我们自己提供了有参构造函数,编译器就不提供默认构造函数,但依然会提供拷贝函数
3.若我们提供了拷贝构造函数,编译器就不会提供其他构造函数
八、深浅拷贝
1.编译器提供的默认拷贝构造函数,做了简单的值拷贝(浅拷贝)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string.h>
using namespace std;
class Person
{
public:
Person(char *name, int age)
{
myName = (char *)malloc(strlen(name) + 1);
strcpy(myName, name);
myAge = age;
}
~Person() //调用析构函数时会重复释放堆区内存,引发错误
{
if (myName != NULL)
{
free(myName);
myName = NULL;
}
}
char *myName;
int myAge;
};
void test()
{
Person p("Louis", 18);
cout << "姓名=" << p.myName << ",age=" << p.myAge << endl;
Person p2(p);
cout << "姓名=" << p2.myName << ",age=" << p2.myAge << endl;
}
int main()
{
test();
system("pause");
return EXIT_SUCCESS;
}
2.深拷贝解决该问题(自己重写拷贝构造函数)
Person(const Person &p)
{
myName = (char *)malloc(strlen(p.myName) + 1);
strcpy(myName, p.myName);
myAge = p.myAge;
};
九、初始化列表语法
class Person
{
public:
// Person(int a, int b, int c)
// {
// ma = a;
// mb = b;
// mc = c;
// }
Person() : ma(10), mb(20), mc(30) //冒号是关键
{
}
int ma;
int mb;
int mc;
};
void test()
{
Person p;
cout << "Ma=" << p.ma << endl;
cout << "Mb=" << p.mb << endl;
cout << "Mc=" << p.mc << endl;
}
十、explicit关键字使用
防止利用隐式法来实例化对象,避免引发歧义
十一、new和delete
class Person
{
public:
Person()
{
cout << "Person构造函数" << endl;
}
~Person()
{
cout << "Person析构函数" << endl;
}
};
void test()
{
Person *p = new Person;
delete p;
}
1.malloc和new的区别
(1)malloc和free属于是库函数,new和delete属于是运算符
(2)malloc不会调用构造函数,new会调用构造函数
(3)malloc返回void*需要强转,new返回的是创建的对象的指针
2.注意事项
(1)不要用void*去接收new出来的对象
(2)堆区开辟数组时候,一定会调用无参构造函数。释放时加一个中括号。
(3)栈上开辟数组,可以没有默认构造
void test2() // 堆区开辟数组等
{
// int * pInt = new int[10];
// 堆区开辟数组时候,一定会调用无参构造函数
Person *pPerson = new Person[10];
delete[] pPerson;
}