前言:在前面我们带大家初步步入了C++,让大家大概知道了他的样子,那今天就可以说我们要正式步入C++的大门了,这一章内容的细节比较多各位学习的时候一定要仔细。
博主CSDN主页:卫卫卫的个人主页
专栏分类:高质量C++学习
代码仓库:卫卫周大胖的学习日记
关注博主和博主一起学习!一起努力!
C++中,类是一种自定义的数据类型,用于封装数据和相关的操作。对象是类的实例,通过实例化类来创建对象。
类包含了属性(成员变量)和方法(成员函数),用于描述对象的状态和行为。成员变量是类的数据成员,用于存储对象的数据;成员函数是类的操作成员,用于实现对象的行为。
C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题,而在C++中C++是基于面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成,通俗的理解C语言好比送外卖我们需要关注如何下单如何拿,而C++只需要关注如何点外卖。
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。比如:之前在数据结构初阶中,用C语言方式实现的栈,结构体中只能定义变量;
class 类的名称
{
//类体:成员变量--属性 成员函数---功能
}
现在以C++方式实现,会发现struct中也可以定义函数,但在C++中类的定义通过关键字class来实现。以下是一个简单的类的定义示例:
class Date//C++中的定义方式
{
public://限定符
void Init(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout <<"year = " << _year <<" month = " << _month <<" day = " << _day << endl;
}
//成员变量在这里声明(并未开辟空间)并没有定义,开了空间才是定义
private://限定符
int _year;
int _month;
int _day;
};
struct Date1//C语言的定义方式
{
int _year;
int _month;
int _day;
};
int main()
{
Date dl;//定义类
dl.Init(2024, 1, 30);//在c++中引用类的函数可以直接和C语言一样加.即可
dl.Print();
return 0;
}
//定义放在类的实现文件Person.cpp中
#include "Person.h"
void Person::Init(int year,int month,int day){
_year = year;
_month = month;
_day = day;
}
在上面这个例子中我们可以看到,C++中定义的类中我们可以放入函数,而C语言不可以,这个我们在后面马上会讲到,在然后各位肯定看到了这里怎么有个public和private这又是什么东西,那么我们接下来就会给大家讲解这两个是用来干嘛的。
封装:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。(初学阶段我个人理解对这个有个大概的概念即可)
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域,下面我们举了一个例子:
class Date
{
public:
void Init(int year,int month,int day);
void Print();
private:
int _year;
int _month;
int _day;
};
void Date::Init(int year, int month, int day)//这里需要指定Init是属于Date这个类域
{
_year = year;
_month = month;
_day = day;
}
void Date::Print()//同理这里需要指定Print属于Date这个域
{
cout << "year = " << _year << " month = " << _month << " day = " << _day << endl;
}
int main()
{
Date dl;
dl.Init(2024, 1, 30);
dl.Print();
return 0;
}
因此在次强调在类体外定义成员时,需要使用 :: 作用域操作符指明成员属于哪个类域。
用类类型创建对象的过程,称为类的实例化
以下是一些示例代码来演示如何实例化类:
#include
class MyClass
{
public:
void myMethod()
{
std::cout << "Hello, World!" << std::endl;
}
};
int main() {
// 实例化类的对象
MyClass obj;
// 调用类的方法
obj.myMethod();
return 0;
}
我们来看看下面这段代码,类中既可以有成员变量,又可以有成员函数,那么一个类的对象中包含了什么?如何计算一个类的大小?
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
char _a;
};
代码分析:
int main()
{
cout << sizeof(A) << endl;
return 0;
}
为什么会是1呢?我们这里就可以猜测一下难到说那个函数是不算类的空间中的大小了嘛?答案是肯定的。那么我们将讲解一下对象的存储方式
不同的对象,成员变量是不同的,但是成员函数是完全相同的,都是执行的一模一样的函数代码,此时成员函数就不需要存放在对象中,而应该存放在公共代码区。
当对象调用成员函数的时候,是去公共代码区找的,而不是去存放对象的空间里面找。
4.一个类的大小,实际就是该类中”成员变量”之和,当然要注意内存对齐注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象,至于其余的成员变量就和结构体的对齐规则一致如果有不懂的可以看我之前的博客结构体对齐规则。
我们来看下面这个代码:
class Date
{
public:
void Init(int year,int month,int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << "year = " << _year << " month = " << _month << " day = " << _day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.Init(2024, 1, 30);
d2.Init(2025, 2, 32);
d1.Print();
d2.Print();
return 0;
}
上面我们定义了两个对象,那么我们在都调用Init和Print函数的时候,编译器又是如何区分是d1和d2两个对象分别的初始化和打印的呢?
这里我们就要提到一个叫做this指针的概念:
因为C++中通过引入this指针解决了该问题,在C++中,this指针是一个隐含的指针,它指向当前对象的地址。当一个类的成员函数被调用时,编译器会自动地将当前对象的地址作为this指针传递给该成员函数。通过this指针,我们可以在成员函数中访问当前对象的成员变量和成员函数。所以刚刚初始化的代码和打印的代码本质上是这样的:
class Date
{
public:
void Init(int year,int month,int day)
{
this->_year = year;//本质上是有一个this指针指向了成员变量
this->_month = month;
this->_day = day;
}
void Print()
{
cout << "year = " << this->_year << " month = " << this->_month << " day = " << this->_day << endl;
}
private:
int _year;
int _month;
int _day;
};
int main()
{
Date d1;
Date d2;
d1.Init(&d1,2024, 1, 30);//这里只是为了方便大家理解,才写的取地址,实际写的过程中不需要前面这&d1
d2.Init(&d2,2025, 2, 32);
d1.Print();
d2.Print();
return 0;
}
接下来我们再来看几道面试的经典题目:
例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是指向的空指针,空指针又指向了Print这个函数,这里我们第一反应肯定是 这空指针传过去能不报错嘛?我们仔细思考一下,这里本质上虽然是传递了空指针的地址,但是是this指针接收了空指针,为什么会报错?所以这里无非是对象的地址是空而已并不会报错。
运行结果:
我们在来验证一下我们的想法
class A {
public:
void Print() {
assert(NULL);
cout << "Print()" << endl;
}
private:
int _a;
};
int main() {
A* p = nullptr;
p->Print();
return 0;
}
我们在这里在函数部分判断一下传过来的是否是空指针,运行结果如下,因此原本的程序并不会报错只是传递的是空指针。
例题2:下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A {
public:
void PrintA() {
cout << _a << endl;
}
private:
int _a;
};
int main() {
A* p = nullptr;
p->PrintA();
return 0;
}
代码分析:这里我们可以看到和刚刚一样传递了空指针过去,但是这次不同的是我们又要访问成员变量,可是空指针怎么可以访问成员变量呢?空指针是不能解引用的这里我们在C语言的指针和结构体阶段都有过学习,这里就不在过多解释了,因此这个代码是错误的。
这里我们再次对this指针做一个总结:
好啦,今天的内容就到这里啦,下期内容预告类和对象(二)构造函数、析构函数等等
结语:今天的内容就到这里吧,谢谢各位的观看,如果有讲的不好的地方也请各位多多指出,作者每一条评论都会读的,谢谢各位。