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()? }
注意:如果在私有权限里声明静态成员函数则在类外无法访问
|