目录
面向对象和面向过程
先抛砖引玉一下,大家都知道面向对象和面向过程的区别吗?有哪些编程语言是面向对象/过程的呢?
先给大家一长串概念知识:
面向对象编程(OOP)和面向过程编程(POP)是两种不同的编程范式。
面向过程编程更关注解决问题的步骤和过程,通过定义一系列函数或方法实现程序的功能。在POP中,数据和行为是分离的,因此它可能需要开发人员自己管理数据。
而面向对象编程则将数据和处理它的方法封装在一起,以创建一个对象。对象是类的实例,类描述了具有相似性质和功能的一组对象。面向对象编程奠定了软件开发中最重要的原则之一:封装。
这意味着封装了代码和数据,并提供了公共接口来与它们进行交互。通过封装,开发人员可以降低代码的复杂性、提高代码可读性,并使代码更易于维护。另外,OOP还支持继承、多态等特性,使得代码更加灵活、可复用和可扩展。
总之,POP适合简单程序和小规模项目,而OOP适合大型、复杂应用程序,并且可以帮助开发人员更好地组织代码,并实现高效、可靠、易于维护的软件系统。
向我们的c++/java/python/go等语言都是面向对象的编程语言,严谨来说,c++不完全是面向对象,因为要兼容c的一些特点,但这不妨碍它具有面向对象的性质。
面试题:面向对象的三大特征:继承、封装、多态。
类的引入
类可以理解为结构体的延申,
struct ListNode
{
int ret = 0;
ListNode* next;//省略了struct
}
不同于typedef,类可以直接使用类名 ,不需要在声明后才能使用定义后的名字。(如果你在结构体上一行typedef当我没说)
类的定义
class classname()
{
//类体
};
类的关键字是class,后面跟着类名,里面可以是成员函数和成员变量,类中允许函数和变量声明一起定义,类中定义的变量或函数可以不考虑顺序。
1.声明和定义全部放在类体中

2.声明定义分离
<test.h>
test.cpp

有几个点需要注意:
1.分离时注意头文件的包含
2.注意缺省只能在声明时给
3.如果想在类外定义成员函数,则需加上“类名 +::“ 的形式,告诉编译器这是类里面的函数而非全局函数
4.注意类结束 } 后的 ;
我们将struct换成class后我们发现编译不通过,这是因为类中存在访问限定符的概念。
访问限定符分为public(公有),private(私有),protected(被保护的)。
特点:
1. public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3. 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4. 如果后面没有访问限定符,作用域就到 } 即类结束。
5. class的默认访问权限为private,struct为public(因为struct要兼容C)
一般情况下,我们将成员函数设为共有,成员变量设为私有
class Stack//声明和定义分离
{
public:
void Init(int n = 10);
void push(int x);
private:
int* _a;//避免混淆
int _capacity;
int _size;
};
可以看到我给每个成员变量前加了_,这样做是为了避免混淆,我们举个日期类的例子

可以看到是可以赋值的,当去掉_后,可能会有错,出现类似“year= year ”这样的场景

类的实例化
就我们刚才定义的Date类成员变量来看,这属于声明还是定义呢?
答案是声明。
再思考另一个问题,声明会开辟空间吗?
答案是不会。
int main()
{
Date::_year = 2021;//error
}
声明是一幅画轮廓,而所谓类的实例化就是填充画的过程,即开辟空间的过程。所以我们不能像刚才那样指定类域来修改成员变量,因为声明根本不会开辟空间,自然也不会储存值。
d1.Init(2023, 5, 1);
d1._year = 2022;//注意实例化后再对值进行操作
目前修改成员变量可以将其变成public就能修改了,后面讲到this指针会有更好的办法。
为什么说成员变量的实例化,而不说成员函数的实例化呢?我们可以看看类的空间大小再做定论。
cout << sizeof(d1) << endl;//内存对齐
测试发现d1的占用12字节的空间,奇怪的是为什么函数没有占用类的空间呢?那是因为成员函数没有再类中。从公有和私有可以推断出,成员函数就像我们画画的工具,而成员变量是每个人画的画,画出来的对象不同但是工具是一样的,这样做的好处就是大大节省了内存开销,真是妙啊!
我们内存中有一块公共的区域叫做代码段,类似公共场所, 而成员函数就是放到了代码段中统一管理。
我们可以试着求一下下面几段代码的类的大小

结果:
为什么仅有函数和空类占的空间是1呢?很好理解,应该说,有这1空间才正常,不然你怎么访问它的地址呢?。这里的一个空间起到的是占位的作用,是不存储有效数据的。只是区分它是否存在的标记。
this指针
既然函数是公共的,对于刚才的日期类初始化函数,我们都以_+name的方式赋值,那如果我创建多个对象d2,d3....如何知道是哪个对象的值呢,为此,编译器做了一件事——添加this指针
void Init(Date *d,int year, int month, int day)
{
this->_year = year;
this->_month = month;
this->_day = day;
}
d1.Init(&d1,2023, 5, 1);//调用函数
可以看到,编译器在实参中添加了对象的地址,在形参中用一个指针来接收。顾名思义,this就是这个对象的成员变量,这样就可以对不同对象独立完成赋值操作。
我们在写代码的时候知道就好,如果不太熟悉,可以将this指针显式地加上,其他的不用加(规定),或者将_去掉,在前面加上this指针也能时编译器分清声明和形参。
this指针是存在于栈区的,随函数栈帧创立而出现,随栈帧销毁而销毁,是一个隐式的形参。 在vs中是存放于静态区的。它也有可能存放于堆区,取决于对象的创建方式。那有人就说了,为什么不存在于代码段呢?
类中的函数确实存在于代码段,但当调用类的成员函数时,仍然需要开辟函数栈帧。这是因为类的成员函数需要存取类的成员变量,这些成员变量的内存地址是在对象的堆栈帧中分配的,而函数栈帧则是用来存放局部变量和函数参数的,同时它也是一种栈式数据结构,用来记录函数调用的过程。因此,即使函数本身存储在代码段中,调用该函数时仍然需要创建函数栈帧来维护调用堆栈。
空指针问题
我们都知道空指针都是不能解引用的,但在类中稍微有点不同。我们在类里添加一个func函数测试一下。
void func()
{
cout << this << endl;
cout << "func()" << endl;
}

我们编译发现,第一个func函数成功运行,程序在调用Init后就终止了,这是为什么呢?这就涉及一个底层指令问题。
这里的解引用不是对空指针进行 解引用,而是去代码段找func这个函数,如果找到,就进入函数体并执行函数栈帧的一系列调用指令。所以func是正常运行,而Init是因为成员函数都默认传递了一个this指针,而在Init函数中赋值的过程中对空指针进行了解引用,所以会导致程序崩溃。
(*ptr).func();
这段代码结果是什么呢?答案还是正常运行。(从侧面体现了 *(p). 和p->是相同的含义)
可能大家还是觉得迷糊,可以先简单理解为编译器的将它们看成一条条指令去运行,在调用类函数的指令就是'.'或'->', 看似不好理解,其实兼容了语法,使之更加合理。
c++和c的一些区别
c++数据和方法都在类里面
c语言数据和方法是分离的且数据访问控制不受限制
也就是说c语言比较随意,在实现一些大型工程时如果不封装可能会出现访问混淆,随意访问可能会引发不可预估的后果。
C语言中的结构体变量的成员可以直接访问,没有访问控制,而C++中的类成员可以使用访问修饰符(public、private、protected)来限制成员变量和成员函数的访问,可以更好地控制数据的访问权限,实现了更加安全的封装。
总的来说,c++和c语言的语法本质没有什么区别,区别在于c++更加安全,高效,这也是c++的三大特性之一 ——”封装“。
本文介绍了面向对象编程的基本概念,包括面向对象与面向过程的区别,类的定义与实例化,以及C++中的访问限定符。类的实例化是创建对象的过程,而this指针用于在成员函数中引用当前对象。文章还讨论了C++与C在数据和方法处理上的差异,强调了C++的封装特性。

为什么仅有函数和空类占的空间是1呢?很好理解,应该说,有这1空间才正常,不然你怎么访问它的地址呢?。这里的一个空间起到的是占位的作用,是不存储有效数据的。只是区分它是否存在的标记。

被折叠的 条评论
为什么被折叠?



