1.封装
1.1封装的意义
封装是C++面向对象三大特性之一。
意义:
1.将属性和行为作为一个整体,表现生活中的事物。
2.将属性和行为加以权限控制。
(类中的属性和行为统称成员,属性又叫成员属性,成员变量。行为又叫成员函数,成员方法)
意义1.
设计类时,将属性和行为写在一起,表现事物。
const double PI=3.14;
class Circle
{
//访问权限:公共权限
public:
// 属性:半径
int m_r;
//行为:获取半径
double zhouchang()
{
return 2 * PI * m_r;
}
};int main ()
{
//实例化(通过一个类,创建一个对象的过程)
Circle cl;//这个圆就是具体的对象
cl.m_r =10;
cout<<"圆的周长是: "<<cl.zhouchang() <<endl;
return 0;
}
意义2.
类在设计时,可以把属性和行为放在不同的权限下,加以控制。
访问权限有三种:
1.public 公共权限
2.protected 保护权限
3.private 私有权限
在类内,都可以访问,在类外,1.可以访问,2.3.不可以访问.(2.3.区别与继承有关,相对来说,3的控制力度最大)
1.2 struct与class的区别
struct默认权限是公共;
class默认权限是私有。
在class中,如果不写public等权限词,默认private,为私有。
2.对象的初始化和清理
像生活中电子产品的出厂设置,和销毁前清空数据的设置
2.1构造函数和析构函数
构造函数就是初始化,析构函数就是清理。这两个函数会被编译器自动调用,我们不写这俩函数,编译器也会提供。
构造函数:主要作用于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无需手动调用。
析构函数:主要用于对象销毁前系统自动调用,执行前一些清理工作。
构造函数语法:类名(){}
1.没有返回值也不写void
2.函数名称与类名相同;
3.构造函数可以有函数,因此可以发生重载;
4.程序在调用对象时候会自动调用,无需手动调用而且只会调用一次。
析构函数语法:~类名(){}
1.没有返回值也不写void
2.函数名称与类名相同,在函数名称前加 ~;
3.构造函数不可以有函数,因此不可以发生重载;
4.程序在销毁对象时候会自动调用,无需手动调用而且只会调用一次。
class person
{
public:
//1.构造函数
person()
{
cout<<"构造函数的调用"<<endl;
}
//2.析构函数
~person()
{
cout<<"person的析构函数"<<endl;
}
};
2.2构造函数的分类及调用
两种分类方式:
按参数分:有参构造和无参构造
按类型分:普通构造和拷贝构造
三种调用方式:
括号法
显示法
隐式转换法
#include<iostream>
#include<string>
using namespace std;class person
{
public:
//1.构造函数
person()//无参构造函数又叫默认构造函数
{
cout<<"无参构造函数的调用"<<endl;
}
person(int a)
{
age=a;
cout<<"有参构造函数"<<endl;
}
person(const person &p)
{
age=p.age ;
cout<<"拷贝构造"<<endl;
}
//2.析构函数
~person()
{
cout<<"person的析构函数"<<endl;
}
int age;
};//构造和析构都是必须有的 实现,如果我们自己不提供,编译器会提供一个空实现的构造和析构
void test01()
{
//括号法
person p1;//默认构造函数调用
person p2(10);//有参 构造函数调用
person p3(p2);//拷贝构造函数调用
//注意事项1:
//调用默认构造函数时,不要加()
//因为加()后,编译器会认为这是函数声明,不会认为在创建对象
//显示法
person p1;
person p2=person(10);//有参构造
person p3=person(p2);//拷贝构造
// person(10);//匿名对象 特点:当前行执行结束后 ,系统会立即回收匿名对象;
//注意事项2;
//不要利用拷贝构造函数来初始化匿名对象,编译器会认为这是声明对象,造成重定义
//person(p3);
//隐式转换法
person p4=10;//相当于 person p4=person (10) 有参构造
person p5=p4;//拷贝构造 person p5=person(p4)
}
int main ()
{
test01();
system("pause");
return 0;
}
2.3拷贝构造函数调用时机
三种情况:
1.使用一个已经创建完毕的对象来初始化一个新对象
2.值传递的方式给函数参数传值
3.值方式返回局部对象
class person
{
public:
//1.构造函数
person()//无参构造函数又叫默认构造函数
{
cout<<"person的默认构造函数调用"<<endl;
}
person(int a)
{
age=a;
cout<<"person的有参构造函数"<<endl;
}
person(const person &p)
{
age=p.age ;
cout<<"penson的拷贝构造函数"<<endl;
}
//2.析构函数
~person()
{
cout<<"person的析构函数"<<endl;
}
int age;
};//使用一个已经创建完毕的对象来初始化一个新对象
void test01()
{
person p1(20);
person p2(p1);
}//值传递的方式给函数参数传值
void dowork(person p)
{
}void test02()
{
person p;
dowork(p);
}
//值方式返回局部对象
person dowork2()
{
person p1;
cout<<(int*)&p1<<endl;
return p1;
}
void test03()
{
person p=dowork2();
cout<<(int*)&p<<endl;
}
2.4拷贝构造函数调用规则
1、默认情况下,创建一个类,编译器会给每一个类至少添加3个函数
默认构造(空实现:函数里没代码)
析构函数(空实现)
拷贝构造 (值拷贝)
2、如果我们写了有参构造函数,编译器就不再提供默认构造,依然提供拷贝构造。
如果我们写了拷贝构造函数,编译器也不会再提供其他普通构造函数。
2.5深拷贝与浅拷贝
class person
{
public:
person(int age,int height)
{
m_age=age;
m_height=new int(height);
cout<<"有参构造函数" <<endl;
}
person (const person &p)
{
cout <<"拷贝函数"<<endl;
m_age=p.m_age ;
//m_height=p.m_height ;编译器拷贝函数默认代码
//深拷贝操作
m_height=new int(*p.m_height) ;
}
~person()
{
//析构函数里释放堆区数据
if(m_height!=NULL)
{
delete m_height;
m_height=NULL;
}
cout <<"析构函数"<<endl;
}
int m_age;
int *m_height;
};void test()
{
person p1(18,180);cout << "年龄 : "<<p1.m_age <<" 身高 : "<<*p1.m_height <<endl;
person p2(p1);cout << "年龄 : "<<p2.m_age <<" 身高 : "<<*p2.m_height <<endl;
}
当用编译器提供的拷贝函数时,就是浅拷贝。如上有参构造函数中,m_height 是用new在堆区开辟的数据.需要在析构函数里手动释放m_height;如果用浅拷贝,p1执行完后会用析构函数释放m_height。后面拷贝函数p2执行完后会再次释放m_height。造成重复释放,出现错误。
这时需要深拷贝,开辟一个新的new数据,这样p2释放new数据时,不会重复释放。
2.6初始化列表
语法:构造函数 ():属性1(值1),属性2(值2)...{}
class person
{
public:
//传统初始化操作
// person(int a,int b,int c)
// {
// m_a=a;
// m_b=b;
// m_c=c;
// }
//
//初始化列表初始化属性
person(int a,int b,int c):m_a(a),m_b(b),m_c(c)
{
}
int m_a;
int m_b;
int m_c;
};void test01()
{person p(30,20,10);
cout << "m_a= "<< p.m_a <<endl;
cout << "m_b= "<< p.m_b <<endl;
cout << "m_c= "<< p.m_c <<endl;
}
2.7类对象作为类的成员
c++中类的成员可以是另一个类的对象,称为对象成员
class A {}
class B
{
A a;
}
B类中有对象A作为成员,A称为对象成员.
2.8静态成员
静态成员就是在成员函数和成员变量前面加入static关键字,称为静态成员
静态成员分为:
静态成员变量:
1.所有对象共享同一份数据
2.在编译阶段分配内存
3.类内声明,类外初始化
静态成员函数:
1.所有对象共享同一函数
2.静态成员函数只能访问静态成员变量
class person
{
public:
//静态成员函数
static void fun()
{
m_A=100;//静态变量被共享
m_B=200;//静态成员函数只能访问静态成员变量,所以函数里加上m_B会报错
//因为无法区分m_B是那个对象的m_B
cout <<"static void fun 调用"<<endl;
}
static int m_A;//静态成员变量 (必须在类内声明,再在类外初始化一下 )
int m_B;//非静态成员变量
} ;int person::m_A =0;
//所有对象共享同一个函数
//两种访问方式
void test1()
{
//1.通过对象访问
person p;
p.fun() ;
//2.通过类名访问
person :: fun()
}
注意:如果在私有权限里声明静态成员函数则在类外无法访问