c++类与对象(一)
前言:
什么是面向对象编程?什么是类与对象?为啥要搞类与对象啊?凭什么c++能支持面向对象编程啊? 每次学东西前问自己几个问题会挺爽的,因为你越是懵逼,你就越想知道答案是什么。希望我的这几篇文章关于类与对象的介绍能让你找到自己理解的答案。
1.面向对象编程(Object-Oriented Programming,简称 OOP)
都听说过c语言是面向过程编程,c++是面向对象编程。二者有什么区别呢,举个栗子:
大家想象一下洗衣服的过程:
面向过程理解:
我们的需求人把衣服在盆里面洗了,用c语言描述并且解决这个问题就是,用数据结构存放衣服,用一系列的函数描述人的洗衣服的动作,然后输出洗衣服的过程和结果在写一个函数,像人的动作有:拿出来,放进去,放肥皂啊等等这些功能就是函数: 如:void put()等总之你会很明显的发现你的每一步都是一步一步解决 去靠近这个问题的答案 然后设计数据结构完成这个问题的答案,
用英语说就是step by step ,过程性的解决答案。
面向对象理解:
了解对象之前我们先说说类: 简单的很其实,我们人是什么类啊? 动物类? so 人就是动物类的具体的实现,这个实现就是他的其中的对象之一,那动物类还有什么对象啊? ok 你说不知道我就难过了,鸟,斑马斑马,bee,这些问你,我是不是动物啊! 那我就是动物类的对象啊。好的,既然你简单的知道了类与对象的实例了,看来你还是有学习c++的天赋的,那我倾囊相授了:
类用c语言的角度来说就是一个结构体的升级,类不仅可以封装数据,还可以放方法(这个方法你也可以理解为函数嘛 不就是你的功能呗 英语中的fun —>函数还有一个意思就是功能嘛)。
好的天赋异禀的你:接下来用类与对象的思想去解决我们洗衣服这个问题吧!--------给你几分钟思考---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------(这是你思考的过程真顺利,我刚好混点字数)
首先我们看看有几个对象吧: 人 在 盆 里面 洗衣服 果然还是被你发现了它藏的这么深! 一共三个 人 盆 衣服 那洗这个动作是谁的行为(方法)? 确实你说的没错就是人的 所以后面设计人的时候得有他的洗这个方法。
好的,所以接下来我们要实现对象是不是得有关于对象的类啊? 对 你说的没错 。
人这个概念是很笼统啊:是什么名字的人在洗衣服啊? 他有一系列属性去描述这个人 ,他的名字啊 学号啊 以及方法 如洗衣服这个方法 。然后在创建人的对象实例化。 在它的属性里面去实现一个方法 去调用衣服这个对象(哪件哪件衣服是有区别的对吧所以衣服也是类 具体的衣服才是对象) 去完成洗衣服这个动作。
总结一下面向对象编程的概念(面试常考哦!别水): 面向对象是基于对象以及对象之间的交互去解决问题的一种编程思想。
类:将具体的事物抽象出来他们的方法和属性的描述。对象:类的实例嘛。
二.类的引入
知道一个事 ,在c语言中我们见过结构体,如单链表:
struct SingleListNode{
int val;// 存放的数据
struct SingleListNode*next;// 存放下一个节点的指针
};
总之结构体里面只能放一些值 指针啊 这些数据的集合不能放方法,而且对于里面的数据要是想修过我们可以直接拿到他的指针然后直接修改,直接将数据暴露给使用者。
在c++中引入类以后,类是不仅仅能方法的还能放函数的,创建类常见的方式:
class +类名{
};
如创建 一个链表类:
class SingleListNode{
int val;// 存放的数据
struct SingleListNode*next;// 存放下一个节点的指针
void Init()
{
val = 0;
next = NULL;
}
};
这里面的val,next指针都是成员变量 这个方法叫做成员函数: 同一个类中的成员函数可以直接访问当前类中的成员变量。那类外也可以随便访问成员变量吗??? (留个?给你先)
解密
我们都知道c++要求兼容c语言的啊,那我问你,脑子为什么贱贱的,不是说错了,那我问你。struct到了c++中会有什么变化: 它跟class有什么区别啊。 先说一个小结论:struct被升级了跟class一样可以被用来创建类: 而且创建这个类以后 这个类的名字就是类名前面不用加struct了。
天命人你要想深刻理解他们的区别,你得学学类的这几个概念!!!否则小小天命人终归是蝼蚁。
1.机缘1---- 类中的限定操作符 public,private,protected
在类中会对一些方法啊属性啊表示公开与不公开给类外的使用,那我问你,数据想成员变量要不要暴露给你随便改动? 那我如果不暴露我你想访问我的数据(成员变量)我暴露啥给你。
------------------------------------恭喜你想到了奖励给你我的理解:类中的限定操作符是对于类外限定的 所以一般封装数据 把方法暴露外界,比如想访问链表某一个节点的数据你可以写一个成员函数: int getval(); 而不是直接访问val的值 你就只能访问但不能修改 但是如果要修改它的值就在设置一个函数 void setval();那具体如何实现 如下:
class stack{private:int myval;stack* mynext;public:
void setval(int val)
{myval = val;
}};
private下面的为私有的,遇到public停下来,public下面的为公有的。
前期你可以理解为protected跟private效果是一样的。
2.机缘2------类的声明与定义
假如你有一个类它有很多属于 属性1,2,3,4,5.。。等等 同时它也有很多方法。。。。。。。 但是你又想迅速理解这个类是什么样的怎么办 ,所以c++中最常用的一招来了
声明与定义分离。
声明 你可以把声明放在.h文件下面 在.cpp文件放定义
test.h
class A {
private:int a1;char a2;int a3;
public:void fun1();};
.cpp 文件
void A::fun1(){cout<<"这是fun1()"<<endl;
}
可以发现你需要把变量名 或者函数名前面放类名+限定符:: 才行
3. 总结
我们现在发现在c++中公有和私有的区别了,当变量被private修饰以后就不能直接被访问可以通过public修饰的函数(或者叫方法)来访问它,class所定义的类所以的成员默认为private的如果没有public修饰的话,虽然struct也可以用来定义类,但是为了兼容c语言所以struct默认的成员为public的。
4.class实现的栈----if 你可以理解下面的代码然后自己写 then 你理解前面的全部咯
#include<iostream>using namespace std;
typedef int DataType ;
class Stack {
private:int _capacity;int _top;DataType* a;
public:void Init(){a = NULL;_capacity = _top = 0;}void Push(DataType val){if (_top == _capacity){int newcapacity = _capacity == 0 ? 4 : 2 * _capacity;a = (DataType*)realloc(a, newcapacity * sizeof(DataType));if (a == NULL){perror("realloc");return;}_capacity = newcapacity;}a[_top] = val;_top++;}void Print(){for (int i = 0; i < _top; i++){cout << a[i] << endl;}}};
void TestStack()
{Stack s1;s1.Init();s1.Push(1);s1.Push(2);s1.Push(3);s1.Push(4);s1.Push(5);s1.Push(6);s1.Print();}
5.诸位---这里面一个很重要的思想你可以理解了---封装
封装就是通过限定符的修饰将数据和操作数据的方法捆绑在一起只暴露了操作数据的接口给外面,这样不用管内部的实现直接使用就行了,既可以提高代码的可读性也可以保护数据。
加深理解: 区别与c语言的结构体,比如以前的栈想访问栈顶 但是也可以直接访问栈顶的其它元素(这是不好的)。
三.类的大小----按照字节序对齐
1.回顾一下sizeof这个操作符吧
对于 int a =1;
sizeof(a); 和sizeof(int); 结果是一样的sizeof操作符计算的是它的占用空间的大小(或者说类型大小)
所以我们计算类型的大小的时候也就是计算类的大小。
这里直接给出结论 它有成员变量,有成员函数? 有人问了 成员变量我可以去按照结构体的字节序对齐原则去写那???成员函数的大小怎么办???
class name{
char*name;
int myage;
public:
void Setage(int age){
myage = age;
}
}
int main()
{
A name1;
name1.myname = "张三";
A name2;
name2.myname = "李四";
}
这里创建了 两个对象 name1 和name2 它的空间图如下:
所以你会发现编译器为了节约空间它认为啊 大家都是出来混的,讲道理是不是 ,所以呢对于成员变量这是你每一个对象独有的,那就是你自己的没人跟你抢,但是对于这些成员函数啊 这就是方法大家可以一起用啊,但是如何在成员函数调用指定的成员变量呢 ,比如张三想调用了成员函数
那他name2.setAge(34);这句代码闯过去内部myage是哪个的的呢显然肯定是张三这个对象的。那是如何实现的这个区分呢----大儒你能想想哦 后面揭秘this指针
2. 计算大小
空类默认大小是1 : (证明我来过) 用来标识这个类
留几个题目给你:
四.this指针---------这家伙就是用来。。。。
1. this指针指向的是当前对象
class Date {
private:int _year;int _month;int _day;
public:/*void Print(Date* const this) 这里编译器帮你隐藏了this指针这个参数他是用来接受对象地址的{cout << this -> _day << endl;}*/// 主函数部分调用它: Date d1 Print(&d1); 但是实际上编译器会自动帮你传参这个地址// 实际上void Print() {cout << this -> _day<< endl;}
};
总之this指针是所有的成员函数 只要你是在类中定义的函数编译器就会隐式生产this指针
this指针的特性: 1. 它是成员函数的一个形参
2. 指向当前对象的地址 ,const this 的指向的地址不能被修改的
3. 它是成员函数的形参所以其实它是不会占用成员变量的空间的
4. 编译器隐式生成的,传参也是。
2.加深理解
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{public:void Print(){cout << "Print()" << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->Print();return 0;
}
结果是 正常运行哦 ,这个时候有人问了 p指向的不是空吗你你你这不是就野指针了吗,实则不然,我们都知道成员函数在公共代码区里面它是如果你只访问这个成员函数是不会访问这个p这个空指针的 但是如果在函数中访问了成员变量这个时候this指针也就是对象指针它指向空,因为p穿过去了 这个时候就会出现野指针问题也就是下面这个代码。
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:void PrintA(){cout << _a << endl;}
private:int _a;
};
int main()
{A* p = nullptr;p->PrintA();return 0;
}
这里就是类与对象(一)的全部内容了 如果你觉得不错麻烦点个赞吧!