在上一篇文章的学习中我们已经了解了从C语言过渡到C++所需要的一些细节的知识,而从这章开始我们正式进入C++的世界。C++不同与C语言是一个面向过程的编程语言,C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。所以我们第一步要学习的就是类与对象
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构中,用C语言方式实现的栈,结构体中只能定义变量;现在以C++方式实现,会发现struct中也可以定义函数。
#include
using namespace std;
struct A1
{
int Add(int a, int b)//函数
{
int c = a + b;
return c;
}
int a;//变量
int b;
};
int main()
{
struct A1 a;
a.a = 1;
a.b = 2;
cout << a.Add(a.a, a.b) << endl;
}
而在C++中则更喜欢将struct更改为class,这两者的区别我们也会在后面中学到,现在我们只需要知道在C++中要把struct改为class即可。
class ClassName//类名
{
//类中的成员
};
类的定义就是如此简单,将struct换为class即可。
class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。
类体中内容称为类的成员:类中的变量称为类的属性或成员变量; 类中的函数称为类的方法或者成员函数。
注意:类有两种定义方式
class A1
{
public:
int Add(int a, int b)//成员函数同时声明加定义
{
int c = a + b;
return c;
}
int a;
int b;
};
2.将成员函数的声明写在.h的文件中,定义写在.cpp的文件中。
#include
using namespace std;
//test.h
class A1
{
public:
int Add(int a, int b);
int a;//变量
int b;
};
//test.cpp
int A1::Add(int a, int b)//在函数前需要加类名::
int c = a + b;
return c;
}
//在我们日常使用时尽量使用第二种方式。
在上面的代码中,大家可能看见了我在类的定义中加了一行public。这既是访问限定符的一种
访问限定符有三种:
1.public(公有) 此限定符修饰的成员可以在类外被直接访问
2.propected(保护)此限定符修饰的成员不可以在类外被直接访问
3.private(私有)此限定符修饰的成员不可以在类外被直接访问
区别三种限定符的权限作用域范围则是:作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止。如果后面没有限定符再出现则是到类的}为止。
而这时class和struct的区别也显现出来了即class的成员默认为private而struct的成员默认为public(因为要兼容C语言)
我们常说面向对象的三大特性是:封装、继承、多态。
在类与对象的学习阶段,我们经常要接触到封装的相关知识。那什么是封装呢?
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
这是官方对于封装的解释,而通俗一点的来说:封装是一种管理方式,它可以更好的让我们使用类。比如在我们日常生活中接触到的电子器件都经过了封装,即我们可以轻易的使用它但是并不了解它的原理,像是在外面套上了一层壳让我们无法了解到内部的结构与细节。
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
class A1
{
public:
int Add(int a, int b);
int a;//变量
int b;
};
int A1::Add(int a, int b)//在类外定义成员函数
{
int c = a + b;
return c;
}
在C语言阶段我们想要计算结构体的大小直接使用sizeof即可,但是在C++中我们引入了成员函数这个全新的概念,那我们该如何计算类的大小呢?
//例如这样的一个成员函数
class A1
{
public:
int Add(int a, int b);
int a;//变量
int b;
};
我们想要计算出类的大小那就要先了解类在内存中如何存储的。
在我们的设想中一共有三种存储方式:
1.对象中包含类的各个成员
2.代码只保存一份,在对象中保存存放代码的地址
3.只保存成员变量,成员函数存放在公共的代码段
想要知道类的存储方式我们可以设计一份代码再计算他们的类的大小即可
#include
using namespace std;
class A2//存储成员函数和成员变量
{
int Add(int a, int b);
int a;
int b;
};
class A3//存储成员函数
{
int Add(int a, int b);
};
class A4//空类
{
};
int main()
{
cout << sizeof(A2) << endl;//8
cout << sizeof(A3) << endl;//1
cout << sizeof(A4) << endl;//1
return 0;
}
- 从这份代码中可以看出,类的大小其实就是成员变量之和,而成员函数则是不占用空间因为他们存在于公共代码区。
- 而对于空类则有一些特殊,它和只有成员函数的类相同只有一个字节存储在内存中,而这个字节也是用来唯一标识这个类的对象。
注意:类的成员变量的计算方法和C语言中结构体的计算发法是相同的也是需要内存对齐的,这里我们就不过多解释。
我们先定义一个类
#include
using namespace std;
class Date
{
public:
void DateInit(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void DatePrint()
{
cout << _year << '/' << _month << '/' << _day << endl;
}
private:
int _year;//这里的_是为了区分成员变量与成员函数中的形参
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.DateInit(2023, 7, 30);
d2.DateInit(2023, 7, 31);
d1.DatePrint();
d2.DatePrint();
return 0;
}
在上面我们知道了成员函数是存放在公共代码区的,那当我们定义两个或者多个对象后用两个不同的对象分别调用这些函数它是怎么对我们进行区分的呢?
例如:在Date类中,我们定义了d1对象和d2对象,那我们调用DateInit和DatePrint后函数是如何知道应该设置d1对象,而不是设置d2对象的呢?
这就是在C++中全新引用的this指针即是:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有“成员变量”的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
- this指针的类型:类类型* const,即成员函数中,不能给this指针赋值。
- 只能在“成员函数”的内部使用
- this指针本质上是“成员函数”的形参,当对象调用成员函数时,将对象地址作为实参传递给 this形参。所以对象中不存储this指针。
- this指针是“成员函数”第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传 递,不需要用户传递
void DateInit(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
//void DateInit (Date* this ,int year ,int month ,int day)
//{
// this->_year = year;
// this->_month = month;
// this->_day = day;
//}
//下面的就是将this指针显式出来的结果,
//但是this指针在成员函数外是无法显式使用的但是在函数内是可以显式使用的。
这篇文章仅仅只是类与对象中最基础的知识,可以说这里的知识也只是为了后面类与对象的学习铺垫而已。