C语言是面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题;而C++是面向对象的,关注的是对象,将一件事情拆分成不同的对象,靠对象之间的交互完成。
类的引入 在C语言中,结构体中只能定义变量,在C++中结构体不仅可以定义变量还可以定义函数。
1.C++兼容C的struct用法
2.C++同时对struct进行了升级。把struct升级成为类。(1结构体的名称可以作为类型;2.可以在里面定义函数),例如下面代码:
#includeusing namespace std; struct Student { void SetStudentInfo(const char* name, const char* gender, int age) { strcpy(_name, name); strcpy(_gender, gender); _age = age; } void PrintStudentInfo() { cout << _name << " " << _gender << " " << _age << endl; } char _name[20]; char _gender[3]; int _age; }; int main() { Student s; s.SetStudentInfo("Peter", "男", 18); return 0; }
在上述代码中,直接用student定义了一个变量s,而在c语言中要想定义变量s必须使用struct student;还定义了一个函数PrintStudentInfo,这也是c语言中的struct无法做到的。
类的定义 class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号。类中的元素称为类的成员:类中的数据称为类的属性或者成员变量;类中的函数称为类的方法或者成员函数。由类定义的叫做对象(不再叫变量)。
总结:类主要包含以下几个部分:关键字(如class),名字,访问限定符(如public,private,protect),成员变量,成员函数。
1.声明和定义全部放在类中,需要注意成员函数如果放在类中定义,编译器可能当做内联函数处理,所以一般情况下短小函数可以在类里面定义,长一点的函数可以在类中声明,在类外定义。
2.声明放在.h当中,类的定义放在.cpp文件中。
- public修饰的成员在类外可以直接被访问。
- protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
- 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
- class的默认访问权限为private,struct为public(因为struct要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别。
C++需要兼容C语言,所以C++中struct可以当成结构体去使用。另外C++中的struct还可以用来定义类。class和struct的区别是struct的成员默认访问方式是public,class的成员默认访问方式是private。
注意class和struct都可以自己添加访问限定符。
将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口和对象进行交互。封装本质上是一种管理。c语言的结构体中,数据和方法是分离的,并没有封装在一起,这样就会太过“自由”。
类的作用域类定义了一个新的作用域,类的所有成员都在类的作用域中。如果要在类外定义成员,则需要使用::作用域解析符指明成员属于哪个类域。例如下列代码:
class Stack
{
public:
void Push(int x)
{}
};
class Queue
{
public:
void Push(int x)
{}
};
虽然Stack和Queue中都有push函数,但是不构成函数重载,因为两个Push函数不在同一个类域。
再如:#在Stack.h文件中声明
#pragma once
class Stack
{
public:
void Init();
void Push(int x);
void Pop();
private:
int* _a;
int* _top;
int* _capacity;
};
#在Stack.cpp文件中定义
#include "person.h"
void Stack::Init()
{
_a = nullptr;
_top = nullptr;
_capacity = 0;
}
void Stack::Push(int x)
{
}
void Stack:: Pop()
{
}
可以看出当声明和定义分开始,需要指明在哪个类域中,如void Stack::Init()等,而不能像c语言中直接定义为void Init()。
注意:虽然在声明成员变量时用了private,但是在定义Init,Push,Pop函数时是可以进行访问的,因为这三个函数是处在类域中,只是放在类外来进行定义。
用类类型创建对象的过程叫做类的实例化。
1.类只是一个模型一样的东西,限定了类有哪些成员,但是并没有分配实际的内存空间来存储它。
2.一个类可以实例化出多个对象,经过实例化出的对象占用实际的物理空间,用来存储类成员变量。
3.所以类就相当于一个房子的设计图,而由类实例化出的对象就像一栋栋房子。设计图不占用实体的空间,房子才占用空间,而房子的作用就是用来住人和放东西的,即实例化出的对象才能实际存储数据占用物理空间。
注意:在上一个代码中,定义的类(class Stack)中的成员变量a,top,capacity均是声明,因为声明和定义的区别在于是否开辟空间,而类本身不占用物理空间,所以类成员变量只是声明。因此用Stack实例化出的Stack s中的成员变量就是定义,以为实例化的对象占用物理空间。
#includeusing namespace std; class Stack { public: void Init() { _a = nullptr; _top = 0; _capacity = 0; } void Push(int x) { } void Pop() { } private: int* _a; int _top; int _capacity; }; int main() { Stack st; cout << sizeof(st) << endl; return 0; } //输出结果为12
因此可以看出类中只保存成员变量,成员函数存放在公共的代码段,所以类对象的大小不考虑成员函数。可以用下图表示:
注意:
class A1 {
public:
void f2()
{
}
};
class A2
{};
上述这两个类的大小虽然没有成员变量只有函数,但是其定义的对象大小不是0,而是1。这一个字节不是为了存储有效数据,而是用来占位,表示对象存在。要不然大小是0就会出现不开空间,那就没有地址。
this指针#includeusing namespace std; class Data { public: void Display() { cout << _year << '-' << _mouth << '-' << _day << endl; } void Setdata(int year, int mouth, int day) { _year = year; _mouth = mouth; _day = day; } private: //结构体成员变量一般会加上一个'_',因为容易与形参混淆。例如strcpy(name, name)。 int _year; int _mouth; int _day; //注意:成员变量可以放在最后,因为类是一个整体,所以不是从下往上找,而是在类里面整体查找。 }; int main() { Data d1, d2; d1.Setdata(2022, 6, 9); d2.Setdata(1998, 11, 24); d1.Display(); d2.Display(); return 0; } //其结果为: 2022-6-9 1998-11-24
Date类中有SetDate与Display两个成员函数,函数体中没有关于不同对象的区分,那当s1调用SetDate函数时,该函数是如何知道应该设置s1对象,而不是设置s2对象呢?
C++ 中通过引入this指针解决该问题,即:C++编译器给每个“非静态的成员函数“增加了一个隐藏的指针参数,让该指针指向当前对象(函数运行时调用该函数的对象),在函数体中所有成员变量的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编译器自动完成。
1. this指针的类型:类类型* const
2. 只能在“成员函数”的内部使用
3. this指针本质上其实是一个成员函数的形参,是对象调用成员函数时,将对象地址作为实参传递给this形参。所以对象中不存储this指针。
4. this指针是成员函数第一个隐含的指针形参,一般情况由编译器通过ecx寄存器自动传递,不需要用户传递。
如下图所示:



