温故知新——C++--封装
参考:
1.https://blog.csdn.net/cherrydreamsover/article/details/81942293
2.https://www.cnblogs.com/jiqing9006/p/9348832.html
3.https://blog.csdn.net/WJ_SHI/article/details/81782643
我们都知道C++有三大特性:封装、继承、多态,之前我总结过继承的知识点,现在来总结一下封装的相关知识!
一、什么是封装?
封装:隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互,将数据和操作数据的方法进行有机结合。
说明:
函数是封装的一种形式:函数所执行的细节行为被封装在函数本身这个更大的实体中,被封装的元素隐藏了它们的实现细节–可以调用一个函数但是不能够访问函数所执行的语句。
访问限定符
(1)public(共有)
(2)protected(保护)
(3)private(私有)
说明:
(1)public成员可以在类外直接访问。
(2)protected和private成员在类外(在此将这两种限定符都可以看成是私有的,在继承出区别)不能够访问。
(3)它们的作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。
(4)class的默认访问权限是private,而struct为public型(因为struct要兼容C)。C++常用的是class
(5)类外:即脱离了类的作用域或者说访问时不在类的成员函数中。
因此C++的封装和函数有一些不同,它能够更加灵活的满足不同对象调用的需求,因此封装相当于是class+访问限定符
注意:访问限定符本质上是给编译器使用的,数据放在内存中是没有任何限制的
来段代码看看:
1 class CDate
2 {
3 public :
4
5 void SetDate(int iYear = 1990, int iMonth = 1, int iDay = 1)
6 {
7 _iYear = iYear;
8 _iMonth = iMonth;
9 _iDay = iDay;
10 }
11
12 void PrintDate()
13 {
14 cout << _iYear << "-" << _iMonth << "-" << _iDay << endl;
15 }
16
17 private:
18 int _iYear;
19 int _iMonth;
20
21 public:
22 int _iDay;
23 };
24 int main()
25 {
26 CDate d;
27 d.SetDate(2016, 3, 2);
28 d.PrintDate();
29 // d._iYear = 2016; // 私有成员变量不能再类外直接访问
30 // d._iMonth = 3;
31 d._iDay = 2;
32 return 0;
33 }
二、类的作用域
局部作用域
全局作用域
类作用域
名字空间作用域
(1) 在类体外定义成员,需要使用 :: 作用域解析符指明成员属于哪个类域。
(2) 在类的作用域外,只能够通过对象或指针借助成员访问操作符.和->来访问类成员,跟在访问操作符后面的名字必须在相关联类的作用域中。
(3)成员变量在类中具有全局作用域。
1 // ConsoleApplication6.cpp : 定义控制台应用程序的入口点。
2 //
3
4 #include "stdafx.h"
5 #include "iostream"
6 using namespace std; //(全局作用域)名字空间作用域
7
8
9 namespace NewSpace
10 {
11 int iValue = 10; // (括号内)名字空间作用域
12 }
13
14 int iValue = 20; // 全局作用域
15
16 class CTest
17 {
18 public:
19 void FunTest(int iValue)
20 {
21 iValue = iValue; //(函数)局部作用域
22 }
23
24 void Print()
25 {
26 cout << iValue << endl;
27 }
28
29
30 private:
31 int iValue; // (类作用域)
32
33 };
34
35
36
37 int main()
38 {
39 CTest test;
40 test.FunTest(30); // 类作用域中函数局部作用域值改为30
41 cout << iValue << endl; // 输出全局变量作用域值 20
42 cout << NewSpace::iValue << endl; // 输出名字作用域 10
43 test.Print(); // 输出类作用域定义的变量值,但未被初始化
44 system("pause");
45 return 0;
46 }
三、类的实例化
举一个例子我们来体会一下类的实例化的过程:
类–>实例化–>对象
图纸–>建造–>别墅
类是抽象的,只是限定了类中有哪些成员,定义了类并没有分配实际的内存来存储它。
CDate d; // 类实例化
//用类类型创建对象的过程
//一个类可以实例化出多个对象,实例化123
四、类对象模型
类中的成员和对象在类中布局格式:
说明:
(1)可以用sizeof来求一个非空类的大小;
(2)空类的大小为一个字节,但是如果该空类变成非空类,例如类中有一个int,则该类的大小计算时,不在计算原来的1。
五、this指针
特性
(1)this指针的类型:类类型* const。
(2)this指针并不是对象本身的一部分,不影响sizeof的结果。
(3)this是一个指针,它时时刻刻指向对象的实例。
(4)this指针的作用域在类成员函数的内部(不严谨)。
(5)this指针是类成员函数的第一个默认隐含参数,编译器自动维护传递。
(6)只有类的非静态成员函数中才可以使用this指针,其它成员函数都不可以。
_thiscall调用约定
(1)_thiscall只能够在类的成员函数上;
(2)参数从右向左压栈;
(3)如果参数个数确定,this指针通过ecx传给被调用者。如果参数不确定,this指针在所有参数被压栈后压入堆栈;
(4)参数个数不定的,调用者清理堆栈,否则函数自己清理堆栈。
this指针是否为空?
void test(person* const this)
{}
void change(person* const this)
{
cout<<name<<endl;
}1234567
当将空的this传给test函数时,test没有调用任何函数,因此执行此函数,没有报错;
当把空的this传给change函数时,通过this指针调用this->name,由于this是空的,因此会出错。
总结:
(1)NULL对象指针可以调用成员函数;
(2)通过对象调用成员函数,对象的指针会被传入函数中,指针名称为this;
(3)NULL对象指针调用成员函数时,只要不访问此对象的成员变量,则程序正常运行;
(4)NULL对象指针调用成员函数时,一旦访问此对象的成员变量,则程序崩溃。
六、编译器如何来识别一个类?
C++之封装 (这篇博文对构造函数有很好的解释)
希望暴露public
希望隐藏private
对象实例化有两种方式,从栈实例化,从堆(new出来的)实例化。
以谁做什么作为核心。
public 放前面,private放后面(属性可以定义为private格式)。
只读属性,只有get方法,没有set方法。
#include <iostream>
#include <string>
using namespace std;
/**
* 定义类:Student
* 数据成员:m_strName
* 数据成员的封装函数:setName()、getName()
*/
class Student
{
public:
// 定义数据成员封装函数setName()
void setName(string name) {
m_strName = name;
}
// 定义数据成员封装函数getName()
string getName() {
return m_strName;
}
//定义Student类私有数据成员m_strName
private:
string m_strName;
};
int main()
{
// 使用new关键字,实例化对象
Student *str = new Student;
// 设置对象的数据成员
str->setName("cpp");
// 使用cout打印对象str的数据成员
cout << str->getName() << endl;
// 将对象str的内存释放,并将其置空
delete str;
str = NULL;
return 0;
}
栈区,存储变量。
new分配的内存,是堆区。
全局区,存储全局变量和静态变量。
常量区,存储常量。
代码区,存储代码。
对象需要初始化,有的只有一次,有的需要初始化多次。
构造函数,会在对象实例化时被调用。
读书,视频,先看思想,读其骨架。细节次之。
都有默认值的构造函数,称为默认构造函数。
一个类可以没有默认构造函数,有别的构造函数也可以实例化对象。
可以全屏观看,看到关键点可以暂停,记录一下。因为屏幕太小,看着眼疼。或者全屏观看的时候,把文本置顶。
C++中,构造函数与类名相同,析构函数前面加一个波浪线。析构函数,可以进行资源释放。
tips:class 声明类,要小写的c。构造函数,析构函数前面,不需要任何修饰。class结尾还需要分号;
#include <iostream>
#include <string>
using namespace std;
/**
* 定义类:Student
* 数据成员:m_strName
* 无参构造函数:Student()
* 有参构造函数:Student(string _name)
* 拷贝构造函数:Student(const Student& stu)
* 析构函数:~Student()
* 数据成员函数:setName(string _name)、getName()
*/
class Student
{
public:
Student() {
m_strName = "jack";
cout<<"Student()"<<endl;
}
Student(string _name) {
m_strName = _name;
cout<<"Student(string _name)"<<endl;
}
Student(const Student& stu) {
cout<<"Student(const Student& stu)"<<endl;
}
~Student() {
cout<<"~Student()"<<endl;
}
void setName(string _name) {
m_strName = _name;
}
string getName() {
return m_strName;
}
private:
string m_strName;
};
int main(void)
{
// 通过new方式实例化对象*stu
Student *stu = new Student("小李");
// 更改对象的数据成员为“慕课网”
stu->setName("慕课网");
// 打印对象的数据成员
cout<<stu->getName()<<endl;
delete stu;
stu = NULL;
return 0;
}
#include <iostream>
#include <string>
using namespace std;
/**
* 定义类:Student
* 数据成员:m_strName
* 无参构造函数:Student()
* 有参构造函数:Student(string _name)
* 拷贝构造函数:Student(const Student& stu)
* 析构函数:~Student()
* 数据成员函数:setName(string _name)、getName()
*/
class Student
{
public:
Student() {
m_strName = "jack";
cout<<"Student()"<<endl;
}
Student(string _name) {
m_strName = _name;
cout<<"Student(string _name)"<<endl;
}
Student(const Student &stu) {
cout<<"Student(const Student &stu)"<<endl;
}
~Student() {
cout<<"~Student()"<<endl;
}
void setName(string _name) {
m_strName = _name;
}
string getName() {
return m_strName;
}
private:
string m_strName;
};
int main(void)
{
// 通过new方式实例化对象*stu
Student stu;
Student stu2 = stu;
// 更改对象的数据成员为“慕课网”
stu.setName("慕课网");
// 打印对象的数据成员
cout<<stu.getName()<<endl;
return 0;
}
Student()
Student(const Student &stu)
慕课网
~Student()
~Student()
(下面这篇博文用struct结构体c的方式封装)
在C语言中可以使用struct(结构体)将相应的数据封装起来,统一使用,同样地在C++中也可以使用struct将相应的属性及方法封装起来,例如下面的程序:
#include <iostream>
using namespace std;
struct S{
int e1, e2;
};
void f(const S& s){ //以引用的方式传递地址
cout << s.e1 << " " << s.e2 << " ";
}
int main(){
S s = {1, 2}; //定义一个S类,并赋值
f(s); //通过向函数f传递s的地址,以输出s中的两个属性
S* z = &s; //定义一个指向s的指针z
z->e1 = 3; //通过指针间接改变s中属性e1的值,注意,针对指针必须使用“->”符号,用来调用结构体中的成员
(*z).e2 = 4; //通过指针指向的地址直接改变s中的属性e2的值,这里则要用"."调用,因为此处是*z
f(s); //再次输出
}
输出结果如下:
上面的程序中main函数中的第一行,初始化(赋值)一个结构体,在C++中称之为构建对象。在上面一个程序中,已经将对象s的所有属性(e1,e2)都放在了同一结构体(S)中,这相当于将所有具有同一性质的物质合并在了一起。例如下面的程序:
#include <cmath> //在C++中有关复数和根运算的函数都在此头文件中
#include <iostream>
#include <iomanip> //在C++中有关小数点精确的函数都在此头文件中
using namespace std;
struct Complex{
double re;
double im; //结构体的属性为实部和虚部——对象为复数
Complex mul(const Complex& z);
double amount(){ //该方法用于计算对象复数的模
cout << "amount() "; //用于确认该函数确实被调用了
return sqrt(re * re + im * im); //计算模的方法,sqrt为求平方根
}
};
int main(){
Complex a = {0, 0};
bool valueChange = true;
for(int i = 1; i <= 8; i++){
double amount = a.amount(); //每循环一次就计算一次模,但是这一值确实每两次才改变一次,因为后面有if控制改变的条件
cout << "|a| = " << showpoint << setprecision(2) << amount << " "; //输出模
valueChange = !valueChange;
if(valueChange){
a.re = i;
a.im = -i;
cout << endl;
}
}
}
输出结果:
在上面的程序中,结构体s中的属性re,im即可以在其本身的方法中被读取使用,也可以在main函数中被直接修改,这样结构体中的数据可被任何一个函数调用或者修改,因此就会造成数据泄露,为了阻止这一情况的产生,C++提供了一些关键词用来保护数据的访问权限:
- private(私有):在这一关键词之后的数据(属性或者方法)均为对象私有的,只能在结构体内部进行修改或访问,其他函数或结构体没有访问权限。
- public(公共):在这一关键词之后的数据(属性或方法)均为公共的,在结构体外的任何函数都可对其访问或修改
- protected(保护):在这一关键词之后的数据均为受保护的类型,只有在该类或由该类派生出来的类中才有访问权限。例如:
#include <iostream>
#include <cmath>
#include <iomanip>
using namespace std;
struct Complex{
private: //关键词private修饰,表示下面的re和im为结构体Complex的私有成员,只能在结构体中被访问或修改
double re;
double im;
public: //关键词private修饰,表示下面的方法(value,amount,getReal,getImag)为公共成员,不仅能在能在结构体中被访问或修改,也可在main函数中被调用
void value(double r, double i){
re = r;
im = i;
}
double amount(){
return sqrt(re * re + im * im);
}
double getReal(){
return re;
}
double getImag(){
return im;
}
};
int main(){
Complex z;
z.re = 3; //产生编译错误
z.im = 4; //产生编译错误,此处re和im为结构体Complex的私有成员,在main函数中无访问和修改权限
z.value(3, 4); //此处合法,方法value在结构体Complex,其拥有访问和修改im和re的权限,而value被关键词public所修饰,在main函数中可以访问或调用
cout << z.re << " + " << z.im << "j"; //产生编译错误
cout << z.getReal() << " + " << z.getImag() << "j"; //合法
cout << endl;
cout << "the amount of this complex number is: " << sqrt(z.re * z.re + z.im * z.im); //产生编译错误
cout << "the amount of this complex number is: " << z.amount(); //合法
}
在C++中如果没有像上述程序中所示的声明,则结构体中所有的属性和方法均视为public。与此相对,C++还提供了另外一个关键词class(类),在class中,所有的属性和方法,如果没有特别声明,则均视为private。如下面程序:
struct S{
int i;
};
class C{
int j;
}
int main(){
S s;
s.i = 4; //合法,结构体中的属性及方法默认为public
C c;
c.j = 4; //产生编译错误,类中的属性及方法默认为private
}
如果要将类中的属性及方法定义成public,则需要先声明。一般地,在一个类中,属性为private型,而访问,调用或修改其的方法为public类。这样在使用数据的同时也做到了对数据的保护。
温故知新——C++--封装的更多相关文章
- 深入js的面向对象学习篇(封装是一门技术和艺术)——温故知新(二)
下面全面介绍封装和信息隐藏. 通过将一个方法或属性声明为私用的,可以让对象的实现细节对其它对象保密以降低对象之间的耦合程度,可以保持数据的完整性并对其修改方式加以约束.在代码有许多人参与设计的情况下, ...
- 温故知新----封装(struct)
上次提到class是最常见的封装,今天发现别人开发的SDK里面有大量的结构体struct 转载: 1. https://blog.csdn.net/a_forever_dream/article/de ...
- 【温故知新】c#事件event
从上一篇文章[温故知新]C#委托delegate可知,委托delegate和事件Event非常的相似,区别就是event关键字,给delegate穿上了个“马甲”. 让我们来看官方定义: 类或对象可以 ...
- 【温故知新】C#委托delegate
在c#的学习过程中,学到委托与事件总会迷糊一段时间,迷糊过后自然而就似懂非懂了~,所以最近我打算把以前所学的迷糊过的知识总结,温故知新,总结记录下来. 首先,我们来看一下msdn对委托的定义: del ...
- 00-Unit_Common综述-RecyclerView封装
自学安卓也有一年的时间了,与代码相伴的日子里,苦乐共存.能坚持到现在确实已见到了"往日所未曾见证的风采".今2018年4月2日,决定用一个案例:Unit_Common,把安卓基础的 ...
- [C#] 简单的 Helper 封装 -- RegularExpressionHelper
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- iOS开发之App间账号共享与SDK封装
上篇博客<iOS逆向工程之KeyChain与Snoop-it>中已经提到了,App间的数据共享可以使用KeyChian来实现.本篇博客就实战一下呢.开门见山,本篇博客会封装一个登录用的SD ...
- Ajax实现原理,代码封装
都知道实现页面的异步操作需要使用Ajax,那么Ajax到是怎么实现异步操作的呢? 首先需要认识一个对象 --> XMLHttpRequest 对象 --> Ajax的核心.它有许多的属性和 ...
- 用C语言封装OC对象(耐心阅读,非常重要)
用C语言封装OC对象(耐心阅读,非常重要) 本文的主要内容来自这里 前言 做iOS开发的朋友,对OC肯定非常了解,那么大家有没有想过OC中NSInteger,NSObject,NSString这些对象 ...
随机推荐
- 【Android】Android开发小功能,倒计时的实现。时间计时器倒计时功能。
作者:程序员小冰,GitHub主页:https://github.com/QQ986945193 新浪微博:http://weibo.com/mcxiaobing 首先给大家看一下我们今天这个最终实现 ...
- 【Android】AndroidStudio关于EventBus报错解决方法its super classes have no public methods with the @Subscribe
作者:程序员小冰,GitHub主页:https://github.com/QQ986945193 新浪微博:http://weibo.com/mcxiaobing 首先说明,以前我用eventBus的 ...
- Stack (30)(模拟栈,输出中间数用set)
Stack is one of the most fundamental data structures, which is based on the principle of Last In Fir ...
- [BUUOJ记录] [GYCTF]EasyThinking
主要考察ThinkPHP6.0的一个任意文件写入的CVE以及突破disable_function的方法. ThinkPHP6.0.0任意文件操作漏洞 理论分析 进入题目是一个简单的操作页面,dirma ...
- List集合对象去重及按属性去重的8种方法-java基础总结系列第六篇
最近在写一些关于java基础的文章,但是我又不想按照教科书的方式去写知识点的文章,因为意义不大.基础知识太多了,如何将这些知识归纳总结,总结出优缺点或者是使用场景才是对知识的升华.所以我更想把java ...
- BootStrap-select插件动态添加option无法显示
问题描述: 在使用bootstrap-select插件时出现下拉框无法显示动态追加的option,经过查看element元素发现,select标签已经append进去了所需的option选项,但是页面 ...
- 20191002思维导图工具MindManager 000 033
- SMBMS
SMBMS(Supermarket Billing Management System ) 目录 SMBMS(Supermarket Billing Management System ) 1. 项目 ...
- 《Redis内存数据库》Redis环境搭建
前言 Redis(Remote Dictionary Server ),即远程字典服务,是一个开源的使用ANSI C语言编写.支持网络.可基于内存亦可持久化的日志型.Key-Value数据库,并提供多 ...
- CentOS7重装yum和python
卸载现有的Python和Yum 1.删除现有Python ##强制删除已安装程序及其关联 rpm -qa|grep python|xargs rpm -ev --allmatches --nodeps ...