一、类(封装)
1.1 类的声明
在实际项目中,一般一个cpp和一个hpp文件配对,描述一个class,class的名字和文件名相同的
在Person.hpp中声明类 Person
class Person
{
public:
int age; //成员变量
int male; //成员变量
float hight; //成员变量
string name; //成员变量
void sleep(); //成员方法(函数)
void printParpam();//成员方法(函数)
float GetRMB();//成员方法(函数)
private:
string girlfriendName; //成员变量
float girlfriendHight; //成员变量
void girlfriendSleep();
protected:
float RMB; //成员变量
void AddRMB();
};
以上写法方法没有实体,不占用内存空间,需在Person.cpp文件中定义方法的实体
//包含头文件
include “Person.hpp”
//定义方法实体
void Person::sleep()
{
}
void Person::printParpam()
{
}
float Person::GetRMB()
{
}
还可以在类的声明中定义函数实体
class Person
{
public:
int age; //成员变量
int male; //成员变量
float hight; //成员变量
string name; //成员变量
void sleep() //成员方法(函数)
{
std::cout << "sleep" << std::endl;
}
void printParpam();//成员方法(函数)
float GetRMB();//成员方法(函数)
private:
string girlfriendName; //成员变量
float girlfriendHight; //成员变量
void girlfriendEat();
protected:
float RMB; //成员变量
void AddRMB();
};
1.2 类的前置声明
类的前置声明:class 类名,类似C语言中先声明后使用,告诉编译器这个类的位置
class Person;
1.3 类的作用域
包含了类的头文件之后,对类的成员变量或成员方法的访问
可以加上作用域 类名:: 表示变量或方法是属于当前这个类
类名::成员变量
类名::成员方法
//定义类中成员方法
void Person::eat1()
{
std::cout << "eat1" << std::endl;
}
二、类的访问权限
2.1 pubilc
public的成员变量,在类的成员方法中可以直接访问
public的成员变量,在任何外部代码中可以通过类的对象来直接访问
public的成员方法,在类内其他成员方法中可以直接调用
public的成员方法,在任何外部代码中可以通过类的对象来直接访问
public就是完全不设防,只要被包含就都可以访问,类似全局变量
2.2 private
private的成员变量,在类的成员方法中可以直接访问
private的成员变量,在任何外部代码中不可以通过对象来直接访问
private的成员方法,在类内其他成员方法中可以直接调用
private的成员方法,在任何外部代码中不可以通过对象来直接访问
private就是对内不设防,类的外部不可以访问,类似静态变量
2.3 protected
protected的成员变量,在类的成员方法中可以直接访问
protected的成员变量,在任何外部代码中不可以通过对象来直接访问
protected的成员方法,在类内其他成员方法中可以直接调用
protected的成员方法,在任何外部代码中不可以通过对象来直接访问
protected就是对内不设防,类的外部不可以访问,类似静态变量
2.4 通过权限的不同,实现面向对象的封装特性
抽象:抽象的一层意思就是隐藏掉不必要的细节
组织和保护:便于整个整体和外部更合理的交流
三、构造函数、析构函数
(1) 构造函数一大功能就是初始化成员变量
(2) 默认构造函数不带参,无初始化功能(编译器自动定义)
(3) 构造和析构函数不需要返回值类型,构造函数可以带参或不带参,析构函数不带参
(4) 若无其他构造函数,则默认构造函数可以省略。若有哪怕1个其他构造函数,则默认构造函数不能省,必须写上。
3.1 函数实体
在.hpp中声明
构造函数:在类的声明中定义 函数名为 类名
析构函数:在类的声明中定义 函数名与 ~类名
class Person
{
public:
int age; //成员变量
int male; //成员变量
int*Pint; //int类型的指针
float hight; //成员变量
string name; //成员变量
Person(); //构造函数
~Person(); //析构函数
void sleep(); //成员方法(函数)
void printParpam();//成员方法(函数)
float GetRMB();//成员方法(函数)
private:
string girlfriendName; //成员变量
float girlfriendHight; //成员变量
void girlfriendSleep();
protected:
float RMB; //成员变量
void AddRMB();
};
在.cpp中进行实体的编写
Person::Person()
{
std::cout << "Person Fun" << std::endl;
}
Person::~Person()
{
std::cout << "~Person Fun" << std::endl;
}
3.2 动态申请、释放内存
通过构造、析构函数进行内存的动态申请和释放
用new分配的对象,用delete释放
申请单个内存: 指针名 =new 类型()
申请长度为len的多个内存 指针名 =new 类型[len]
delete 指针名:释放单个内存
delete[] 指针名:释放多个内存
单个元素的动态申请、释放
Person::Person()
{
Pint = new int();//构造函数中创建动态内存
// pint = new int(22); //构造函数中创建单个内存并赋值为22
}
Person::~Person()
{
delete pint; //释放单个元素
}
多个元素的动态申请、释放
Person::Person()
{
pint = new int[len];//构造函数中创建动态内存
}
Person::~Person()
{
delete[] pint; //释放单个元素
}
3.3 构造函数的重载、形参默认值
class Person
{
public:
int age; //成员变量
int male; //成员变量
int*Pint; //int类型的指针
float hight; //成员变量
string name; //成员变量
Person(); //构造函数
Person(int setage);//构造函数重载
Person(string stename ,int steage ,bool setmale );//构造函数重载
~Person(); //析构函数
void sleep(); //成员方法(函数)
void printParpam();//成员方法(函数)
float GetRMB();//成员方法(函数)
private:
string girlfriendName; //成员变量
float girlfriendHight; //成员变量
void girlfriendSleep();
protected:
float RMB; //成员变量
void AddRMB();
};
构造函数的重载
构造函数可以重载(overload),析构函数不需要重载.
在声明对象时,C++语法会自动调用构造函数(类似钩子函数)
声明对象时传入默认参数,则调用与传入参数匹配的重载构造函数
Person p1; //调用构造函数 Person()
Person p2(25); //调用重载后的构造函数 Person(int setage)
构造函数形参默认值
(1)class声明时可以给函数形参赋值一个默认值,实际调用时若不传参就使用默认值
(2)方法实现时形参可以不写默认值,但是实际是按照声明时的默认值规则的
(3)有默认值情况,要注意实际调用不能有重载歧义,否则编译不能通过
3.4 初始化列表
(1) 一般用于带参构造函数中,用来给属性传参赋值
(2) 成员初始化列表和构造函数之间用冒号间隔,多个列表项之间用逗号间隔
(3) 初始化列表可以替代构造函数内的赋值语句,达到同样效果
通过构造函数进行普通初始化
Person::Person(string stename ,int steage ,bool setmale )
{
this->name = stename ;
this->age = steage ;
this->male = setmale ;
}
构造函数,通过初始化列表进行初始化
Person::Person(string stename,int steage,bool stemale):name(stename),age(steage),male(stemale) //初始化列表项
{
pint = new int[this->len];//构造函数中创建动态内存
}
初始化对象时传入三个参数,调用重载后的构造函数
string name = "boun";
Person P1(name,25,1);
3.5 为什么需要构造、析构函数
(1) 构造函数可以看作是对象的初始化式
(2) 构造函数为对象完成动态内存申请,同时在析构函数中再释放,形成动态内存的完整使用循环。
(3) C语言中struct无构造函数概念,所以struct中需要用到动态内存时必须在定义struct变量后再次单独申请和释放,而这些操作都需要程序员手工完成。
(4) C++ class的构造和析构特性,是C++支持面向对象编程的一大语言特性。
四、构造函数的深拷贝、浅拷贝
4.1 拷贝构造函数
(1) 拷贝构造函数是构造函数的一种,符合构造函数的一般性规则
(2) 拷贝构造函数的引入是为了让对象在初始化时能够像简单变量一样的被直接用=来赋值
(3) 拷贝构造函数不需要重载,他的参数列表固定为const classname & xx
(4) 拷贝构造函数很合适用初始化列表来实现
Person::Person(const Person & p):name(p.name),age(p.age),male(p.male)
{
std::cout << "copy Person" << std::endl;
}
Person P1(name,25,1);//直接初始化
Person P2(P1); //间接初始化,用已创建的对象初始化新创建的对象
4.1 浅拷贝
(1) 在构造函数内部,不涉及到内存的动态申请和释放,就是浅拷贝
(2) 只有普通成员变量初始化的拷贝构造函数,就是浅拷贝
(3) C++会自动提供一个全部普通成员被浅拷贝的默认拷贝构造函数
(4) 浅拷贝在遇到有动态内存分配时就会出问题,因为默认的拷贝构造函数内部没有内存的申请 (一次申请,两次释放 ),
Person::Person(const Person & p):name(p.name),age(p.age),male(p.male)
{
std::cout << "copy Person" << std::endl;
}
4.2 深拷贝
(1) 深拷贝,深的意思就是不止给指针变量本身分配内存一份,也给指针指向的空间再分配内存(如果有需要还要复制内存内的值)一份
(2) 一般如果不需要深拷贝,就不用重写拷贝构造函数,所以提供了的基本都是需要深拷贝
(3) 拷贝构造函数不需要额外的析构函数来对应,用的还是原来的析构函数
Person::Person(const Person & p):name(p.name),age(p.age),male(p.male)
{
pint = new int[len];
std::cout << "copy Person" << std::endl;
}
五、struct和class的区别
5.1 C和C++中struct的区别
(1) C中不支持成员函数(只能通过函数指针成员变量间接支持),而C++源生支持。
(2) C中不支持static成员,而C++中支持。
(3) 访问权限,C中默认public,C++中默认public,但是可以显式指定public/private/protected三者之一
(4) 继承特性上,C中不支持(只能通过结构体包含来间接实现),而C++源生支持,且struct和class可以互相继承
(5) 初始化方面,C中靠初始化式(gcc扩展了初始化语法),而C++靠构造函数所以初始化更自由可定制化
5.2 C++中struct和class的区别
(1) 默认成员权限,struct默认public,class默认private
(2) 继承关系的权限管控,struct默认public,class默认private
(3) struct和class交叉继承时,默认的权限管控取决于子类(派生类)而不是父类(基类)
(4) 模板相关使用都用class,而不用struct了
六、const 、 mutable、inline
6.1 const 修饰形参、变量
表示当前被修饰的参数为只读不可对其进行修改,只能作为右值使用
const int a = 23;
Person::Person(const Person & p)
{
this->name = p.name;
this->age = p.age;
this->male = p.male;
std::cout << "copy Person" << std::endl;
}
6.2 const 修饰常函数- Type FunName() const
class的成员函数承诺在函数内部不会修改class的任何成员变量,注意是任何一个
常函数内部不能改变任何class的成员变量且常函数只能调用常函数不能调用任何非常函数
//void ConstFun()const ;
void Person::ConstFun() const //常函数 内部不能改变任何类的成员变量 且常函数只能调用常函数 不能调用 非 ,常函数
{ //在常函数中,偶尔会修改变量,那就需要在定义变量的 类声明前面 加上 mutable 关键字进行打洞 这样就可以在常函数内修改
std::cout <<"ConstFun" << std::endl;
}
6.3 mutable—打洞
在常函数中,偶尔会需要修改变量,那就需要在定义变量的前面 加上 mutable 关键字
对变量进行打洞 这样就可以在常函数内,对加上mutable 修饰的关键字进行修改
mutable int age;
6.4 inline
(1) 类的声明中直接写函数体,则此函数会被编译器inline化处理
(2) 类的声明中正常处理,而成员函数的实现中加inline
(3) 被inline修饰的成员函数应该放在hpp中而不是cpp中,因为inline是在编译时替换的