文章目录
面向过程与面向对象
面向过程
C语言是面向过程的,关注的是过程,就好比在外卖系统中,关注的是下单,接单,送餐这三个过程,每一个过程都是靠函数来实现的。
面向对象
类似于java,就是纯面向过程的语言,它关注的是商家,用户,骑手这三方以及他们的交互。是靠类的设计与类之间的关系实现的。
而我们的C++是既可以面向对象也可以面向过程的(兼容C)
面向对象的三大特性
封装,继承和多态。
类的引入
关键字
定义类可以使用两种关键字:struct和class。
两者定义类的区别在于,在不使用访问限定符的情况下,struct定义的类中的内容默认为公有(主函数中可以访问类中内容),而class定义的类中内容默认为私有(主函数中不能访问类中内容)。
访问限定符
访问限定符一共有三个:pulic(共有),protected(保护),private(私有)
1.public修饰的成员在类外可以直接被访问。(公有)
2.protected和private修饰的成员在类外不能直接被访问。(私有)
3.访问权限作用域从该访问限定符出现的位置到下一个访问限定符出现为止,如果下面没有访问限定符,那么就到类结束为止。
4.class的默认访问权限为private(私有),struct为public(公有)。
5.在实际定义时,不建议使用默认访问权限,尽量使用访问限定符。
类的定义
在C语言中,struct可以用来定义结构体,但是结构体中只能有变量不能有函数,但是在类中,既可以有变量也可以有函数。
如果一个类中没有函数,在C++中可以既认为它是结构体也可以认为它是类,同时满足结构体和类的使用规则。
struct Student
{
char _name[10];
int _age;
int _id;
void Init(const char* name, int age, int id)
{
strcpy(_name, name);
_age = age;
_id = id;
}
void Print()
{
cout << _name << endl;
cout << _age << endl;
cout << _id << endl;
}
};
这就是使用struct关键字定义的名为Student的类,分为成员变量区和成员功能(函数)区。
注意成员函数里可以访问成员变量,当参数名相同时为了避免无法区分,在定义成员变量时通常加一个_
如果将成员变量名_year改成year,则Init函数中的year=year这条语句中,两个year都将表示形参的year(就近原则),相当于自己给自己赋值,没有达到给对象中year赋值的目的,若想要给对象中year赋值,则需要这样写:Data::year=year
由于面向对象编程具有封装意识,所以定义类的时候尽量使用class来进行定义,先将其置为私有(主函数不能访问其中内容),在使用访问限定符将某一部分置为公有。
通常将成员函数置为公有,而将成员变量置为私有。
class Student
{
private:
char _name[10];
int _age;
int _id;
public:
void Init(const char* name, int age, int id)
{
strcpy(_name, name);
_age = age;
_id = id;
}
void Print()
{
cout << _name << endl;
cout << _age << endl;
cout << _id << endl;
}
};
其中private作用的范围主函数不能访问,public作用的范围主函数可以进行访问。
注意类中的函数可以访问类中的变量。与访问限定符无关。
类的使用
在主函数中,类的使用方法和结构体是一样的,无论是调用参数还是调用函数都是"."操作符。
int main()
{
Student s1;
s1.Init("zhangsan", 1, 2);
s1.Print();
}
定义的s1等变量我们习惯上称之为对象。
类的作用域
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类体外定义成员,需要使用::作用域解析符指明成员属于哪个类域。
比如在头文件中这样声明:
class Student
{
private:
char _name[10];
int _age;
int _id;
public:
void Init(const char* name, int age, int id);
void Print();
};
在.cpp文件中这样引用书写函数:
void Student::Init(const char* name, int age, int id)
{
strcpy(_name, name);
_age = age;
_id = id;
}
void Student::Print()
{
cout << _name << endl;
cout << _age << endl;
cout << _id << endl;
}
注意这里的引用方式。
类的存储方式
类中既包含成员变量也包含成员函数,但是在内存中只存储成员函数而不存储成员变量,这是为了防止多个对象调用成员函数造成浪费。
成员函数存储在公共代码区中。
在计算对象的大小时,只需要计算成员对象所占空间的大小,计算方式与C语言计算结构体大小是相同的。C语言学会自定义类型这一篇足够了(万字呕心沥血总结)【致敬:我们仍然相信光】
注意,当类中没有成员变量时,类所占的大小为1字节,这1byte不存储有效数据,只为了占位,表示对象存在。
this指针
this指针的引入
来看下面一段代码:
class Data
{
private:
int _year;
int _month;
int _day;
public:
void Init(int year, int month, int day)
{
_year = year;
_month = month;
_day = day;
}
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
}
};
int main()
{
Data d1;
Data d2;
d1.Init(2022, 2, 25);
d2.Init(2022, 2, 26);
d1.Print();
d2.Print();
return 0;
}
我们都知道,在定义对象的时候,对象中只存储成员变量而不存储成员函数,那么在调用Print函数时,编译器是如何知道打印的是d1中的值,还是d2中的值呢?
这就需要引入this指针这个概念,其实Print()函数中,并不是没有参数,而是参数被隐藏了,编译器处理的真实情况是:d1.Print(&d1),而接收d1的地址的指针我们称为this指针,它的类型为**const Data* 名为this。即类中定义的函数真实是:Print(const Data this)。
而在函数内部,打印的时候,其实打印的是this->_year,this->_month。
同理在Init函数中,也会将this指针传入,真实传入实际上是:d1.Init(&d1,2022,2,25)
注意:虽然编译器默认如此,但是不能自己像这样将this指针写进去d1.Init(&d1,2022,2,25),d1.Print(&d1),Print(const Data this),如果一定要写的话,可以在函数内部去写this->_year,this->_month。
this指针的特性
1.this指针的类型:const 类名*。
2.只能在成员函数内部使用。
3.this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针。this指针通常存放在栈中,有些编译器也会存在寄存器里。
this指针例题
代码打印的结果是什么:
例题1
class A
{
public:
void Show()
{
cout << "Show()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Show();
}
这段代码可以成功进行打印,注意虽然p是空指针,但是也是可以访问到类中的方法的,但是不能访问到变量,这里this指针接收的就是空指针,只是接收也没有对其进行解引用,所以是可以正常打印的。
例题2
class A
{
public:
void PrintA()
{
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
}
同理,这里this指针接收的是p的地址,但是在函数Print中,对this指针进行了解引用(this->_a)所以程序会崩溃。