类的简述
结构体(struct):只能封装属性(数据)
类(class):将属性(数据)和行为(函数)封装在一起
类中的成员权限
class xxxx
{
public: //公有的,类内外皆可访问
protect: //保护的,类外不可访问,类内和子类可访问
private: //私有的,类外和子类不可访问,类内可访问
};
利用public和private的关系
给成员变量设置为只读权限
class xxxxx
{
public:
int ReadOnly()
{
return readonly;
}
private:
int readonly;
}
给成员变量设置为只写权限
class xxxxx
{
public:
void WriteOnly(int value)
{
writeonly=value;
}
private:
int writeonly;
}
以上两者结合(两个函数)即可给成员变量设置为可读可写权限
类的构造和析构
构造:创建对象(类变量)时,对对象进行初始化
析构:销毁对象时,对对象进行清理释放
一般要人为提供,若不提供,编译器也会提供(不会做任何操作),两者都不需要人为调用
构造函数:
- 没有返回值
- 函数名和类名一致
- 有参数,可多个
- 可函数重载
- 创建对象时自动调用
析构函数:
- 没有返回值
- 函数名=~类名
- 没参数
- 不能重载
- 销毁对象之前自动调用
构造函数的分类:
无参构造和有参构造
普通构造和拷贝构造
拷贝构造:类名(const 类名 &obj)
- 若定义了拷贝构造,系统不再提供默认拷贝构造
- 默认的拷贝构造就是值的拷贝
- 创建对象时,不能调用对应的构造函数,则创建对象失败
- 调用时机:旧对象初始化新对象
形参是一个对象(非引用)
返回的局部对象
class Person
{
public:
int age;
string id;
//构造可重载(多个)
Person()
{
cout<<"无参构造"<<endl;
}
Person(int a, string b)
{
cout<<"有参构造"<<endl;
age=a;
id=b;
}
Person(const Person &p)
{
cout<<"拷贝构造"<<endl;
age=p.age;
id=p.id;
}
};
调用构造函数的方法
void test01()
{
//普通调用
Person p1; //无参构造,不能括号法
Person p2(1,"123456"); //有参构造,括号法
Person p3(p2); //拷贝构造,括号法
}
void test 02()
{
//匿名对象调用
Person (); //无参构造,括号法
Person (1,"123456"); //有参构造,括号法
Person (p2); //定义时不允许
}
void test03()
{
//显示法调用
Person p= Person(); //无参构造,匿名对象被命名为p
Person p1= Person (1,"123456"); //有参构造,匿名对象被命名为p1
Person p2= Person (p1); //拷贝构造,使用时允许匿名对象被命名为p2
}
void test04()
{
//隐式法调用
//不能调用无参构造
Person p1={1,"123456"}; //有参构造,小括号变大括号
Person p2=p1; //拷贝构造,直接写对象名
//注:若想不被隐式调用,在构造函数前加上explicit关键字
}
编译器提供的3个函数
- 无参构造函数(无参,函数体空)
- 析构函数(无参,函数体空)
- 拷贝构造(有参,函数体内作值拷贝)
浅拷贝和深拷贝
浅拷贝
class Person
{
public:
int age;
char *name;
Person(int a, char *b) //有参构造
{
age=a;
name= (char *)malloc(strlen(b)+1);
strcpy(name,b);
}
Person(const Person & p) //浅拷贝
{
age= p.age;
name= p.name; //只做地址值的拷贝
}
~ Person()
{
if( name != NULL)
{
free( name); //同个地址只能被释放一次
name= NULL;
}
}
};
深拷贝
拷贝构造函数重写:
Person(const Person & p) //深拷贝
{
age= p.age;
name= (char*)malloc(strlen( p.name)+1); //重新申请地址
strcpy(name, p.name);
}
构造函数的初始化列表
class Person
{
public:
//先定义m1,m2,m3,再赋值a,b,c
Person(int a,int b, int c)
{
m1=a;
m2=b;
m3=c;
}
//定义
int m1;
int m2;
int m3;
}
对比:
class Person
{
public:
//先声明m1,m2,m3,再定义(定义的顺序与声明的顺序一致)并赋值a,b,c
Person(int a,int b, int c) :m1(a),m2(b),m3(c)
{
}
//声明
int m1;
int m2;
int m3;
}
多个对象构造(类的嵌套)
在类内先定义,只能调用无参构造
在类内先声明,初始化列表可调用有参构造,构造顺序先构造嵌套里面的类,按声明顺序,再构造嵌套外面的类,析构顺序与构造相反。
动态对象申请
molloc动态申请对象时,不会调用构造函数
Person *p=(Person *) malloc( sizeof( Person));
free释放动态对象时,也不会调用析构函数
free(p);
new动态申请对象时,会调用构造函数
Person *p=new Person; //调用无参构造
Person *p=new Person(10); //调用有参构造
void *p=new Person;//调用无参构造,但delete时不能调用析构
delete释放动态对象时,会调用析构函数
delete p;
申请数组空间:
Person *p=new Person[10]; //只能调用无参构造
delete [ ]p;
类的静态成员static
静态成员变量:
- 不能在类内初始化,类内只能声明,定义初始化在全局,声明只是限制作用域
- 在内存中只有一份,多个对象共享一个静态成员变量
- const修饰静态成员变量时,可在类内定义初始化,且静态成员函数可访问
class Person
{
public:
static int a; //声明,在编译阶段分配内存(静态全局区),所以访问时可通过作用域(Person::a)或定义一个对象p(p.a)
const static b=100; //定义并初始化,在编译阶段分配内存(常量区),同上
}
int Person::a=10; //定义初始化
静态成员函数:
- 能访问静态成员变量,不能访问普通成员变量
- 可通过作用域( Person:: show( ) )或实例化对象( p.show( ) )访问
class Person
{
public:
int a;
static int b;
static void show()
{
cout<<a<<endl; //不可访问
cout<<b<<endl; //可访问
}
}
int Person::b=10;
单例模式:
一个类只能实例化一个对象
- 无参构造函数私有化(不能实例化一个对象)
- 拷贝构造函数私有化
- static修饰一个类的指针并私有化(声明),该指针指向new出的唯一对象(要在全局定义初始化)
- 提供一个只读的接口访问指针
成员变量/函数的存储
- 空类的大小为1字节
- 普通成员变量占用类的实例化对象空间大小
- 普通成员函数不占用类的实例化对象空间大小,存在代码区
- 静态成员变量/函数不占用类的实例化对象空间大小,变量在静态全局区,函数在代码区
this指针
类的成员函数后面都默认加了一个指针常量( Person * const this),即this指针,它指向执行调用该函数的对象
const修饰的成员函数(修饰的是this,为常量指针常量const Person * const this),即为常函数,不能通过this指针修改this指向对象的内容
class Person
{
public:
int a;
void show( )
{
cout<<a<< endl; // 即a为this->a
}
}
void test( )
{
Person p;
p. show( ); // this指向了p
}
友元
全局函数成为类的友元
使该函数可以访问类的私有成员
class Person
{
friend void print( Person &p); //声明为友元
public:
Person(string name, int password)
{
this->name=name;
this->password=password;
}
string name;
private:
int password;
};
void print( Person &p) //全局函数
{
cout<<p.password<<endl; //访问私有成员
}
类成为另一个人类的友元
class Data; //先声明
class Person
{
public:
Person(string name, int password); //函数另一写法:类内声明,类外定义
Data *p; //此时类Data只有声明,不能实例化,用指针
void show( )
{
cout<<b->name<<endl;
cout<<b-> password<<endl; //友元后,类Data私有的成员可被类Person访问
}
}
class Data
{
friend class Data; //类Person成为类Data的友元,类Person的函数能访问类Data
friend void show( ); //类Person的成员函数show()成为类Data的友元, Person::show()函数能访问类Data
public:
Data(string name, int password)
{
this->name=name;
this->password=password;
}
string name;
private:
int password;
}
//类外定义,注意加上作用域
Person::Person(string name, int password)
{
p=new Data(name, password);
}