C语言是面向过程的,关注的过程,分析出求解问题的步骤,通过函数调用逐步解决问题。 C++是基于面向对象的,关注的对象,将一件事情拆分成不同的对象,靠对象间的交互完成。
面向对象编程是一种常用的编程思维,它强调万物皆为对象,因此在编程时我们可以把现实世界中的事物抽象成程序中的对象,从而实现软件的设计与开发。与传统的函数编程不同,面向对象编程注重将数据与行为封装在一起,也就是对象包含数据状态,还包含可调用的行为方法。
面向对象编程的特点在于,它具有封装,继承和多态三大特性,封装意味着对象的状态和行为进行封装,使其对外只需要暴露出必要的接口,从而提高了安全性和可维护性;继承是指某个对象可以继承另一个对象的特性,从而快速构建相似属性的对象;多态是指同一种行为在不同的对象上具有不同的表现形式,即在不同的情境下,同一方法可以被不同的对象进行调用。
总之面向对象是一种强大的编程方法,它具有高度的封装性、灵活的继承性和强大的多态性,通过使用对象作为程序的基本处理单元,实现了数据和行为高度有机结合,可以使程序更加高效,结构清晰,并方便管理和扩展。
C语言结构体中只能定义变量,在C++中,结构体不加可以定义变量,也可以定义函数。比如:
typedef int DataType;
struct stack
{
int top;
int capacity;
DataType* array;
void Init() {
top = 0;
array = (DataType*)malloc(sizeof(DataType) * 4);
if (array == nullptr) {
perror("malloc");
return;
}
capacity = 4;
}
void Push(DataType x) {
if (top == capacity) {
//扩容
DataType* tmp = (DataType*)realloc(array, sizeof(DataType) * (capacity * 2));
if (tmp == nullptr) {
perror("realloc");
return;
}
array = tmp;
capacity *= 2;
}
array[top] = x;
top += 1;
}
DataType Top() {
if (top == 0) {
cout << "stack is empty";
}
return array[top - 1];
}
};
int main()
{
struct stack ss;
ss.Init();
ss.Push(1);
ss.Push(1);
ss.Push(1);
return 0;
}
用C语言实现的栈,结构体中只能定义变量,而现在用C++实现,会发现struct
中也可以定义函数。
同时我们也发现,我们再也不用像C语言那样在传个结构体类型到函数中了,直接就可以使用结构体中的变量。
不过在C++中,我们更倾向于用class来定义类。
class ClassName{
//类的组成成分:成员变量和成员函数
};
class为定义类的关键字,ClassName为类的名字,{}中为类的主体。注意分号就是了。 类体中的内容称为类的成员:类中的变量称类的属性或成员变量;类中的函数称为类的方法或者成员函数。 类的两种定义方式
class TestClass{
public:
void Print(){
cout<<_a<<_b<<_c<<endl;
}
private:
char* _a;
int _b;
int _c;
};
.h
文件中,成员函数定义在.cpp
文件中,注意:成员函数名前面需要加类名::
//test.h
class TestClass(){
public:
void Print();
private:
char* _a;
int _b;
int _c;
};
//test.cpp
void TestClass::Print(){
cout<<_a<<_b<<_c<<endl;
}
在一般情况下,更期望采用第二种方式。
类中成员变量命名建议:建议在命名前或者后加上下划线。
name -> _name
age -> _age
这样的话,可以很好的类中的成员变量和其他变量。
C++实现封装的方式:用类将对象的属性于方法结合在一块,让对象更加完善,通过访问权限限制性的将其接口提供给外部的用户使用。
}
截止。将数据和操作方法进行有机结合,隐藏对象的属性和实现细节,仅公开对外接口和对象进行交互。 封装的本质是一种管理,让用户更方便使用类。 就像诸多的电子产品,即使内部结构复杂,但对于使用者来说,不需要区关心它们的内部核心部件,只需要操作其暴露在外部的按钮就可以了,让用户与其完成交互。 在C++语言中实现封装,可以通过将数据以及操作数据的方法进行有机的结合,通过访问权限来隐藏对象内部的细节,控制那些方法可以在类外部直接被使用。
类定义了一个新的作用域,类的所有成员都在类的作用域中。在类外定义成员时,需要使用::
作用域操作符来指明成员属于哪个类域。
class Person
{
public:
void PrintPersonInfo();
private:
char name_[20];
char age_;
};
//指定类域
void Person::PrintPersonIn()
{
//...
}
用类类型创建对象的过程,称为类的实例化
int main()
{
Person yui;//实例化对象
yui.age_ = 18;//初始化
return 0;
}
Person类是没有空间的,只要Person类实例出的对象才有具体的年龄。
我们知道类中可能会存在成员函数和成员变量。
class test
{
public:
void print()
{
cout<<_a<<endl;
}
private:
int _a;
}
当我们用类初始化对象后,对象中会存储什么呢?是成员变量和成员函数都存储,还是只存储成员变量呢? 在我们初始化对象时,每个对象的成员变量是不同的,但是函数是相同的,如果我们的对象需要存储成员函数,那相同的代码保存多次,就会造成空间的浪费。为此我们可以只存储成员变量,在把成员函数放在公共的代码区。
#include <iostream>
using namespace std;
class a
{};
class b
{
private:
int a_;
};
class c
{
public:
void print()
{
cout << a_;
}
private:
int a_;
};
int main()
{
cout << sizeof(a)<<endl<<sizeof(b)<<endl<<sizeof(c);
return 0;
}
观察代码,你觉得打印结果会是什么呢?
1 4 4
可能第一个会有些失误,为什么会是1呢? 这是因为空类比较特殊,编译器会给一个字节来唯一标识这个类的对象。
结构体的第一个成员永远放在偏移量为0的位置
从第二个成员开始,往后的每个成员都要对齐到某个对齐数的整数倍处。对齐数:结构体成员自身的大小和默认对齐数的较小值。VS的默认对齐数是8。gcc没有默认对齐数,对齐数就是结构体成员的自身大小
结构体的总大小,必须是最大对对齐数的整数倍,最大对齐数是:所有成员的对齐数的最大的值
如果嵌套了结构体,嵌套的结构体对齐到自己最大对齐数的整数倍处
在我的这篇文章有关于内存对齐的较详细的讲解:深度刨析自定义类型(结构体、枚举、联合)
在 C++ 中,this
指针是一个隐含的指针,它指向当前对象的地址。它在类的成员函数内部可用,允许访问对象的成员变量和成员函数。以下是关于 this
指针的一些关键点:this
指针常用于区分成员变量和参数名相同的情况下
class stu
{
public:
void Init(int age)
{
this->ags = age;
}
private:
int ags;
};
成员函数
的形参·,当对象调用成员函数时,将对象地址作为实参传递给this形参,所以对象中不存储this指针。成员函数
第一个隐含的指针形参,一般情况下,由编译器通过ecx寄存器自动传递,不需要用户传递。练习1: 下列程序的运行结果:1.编译报错 2. 运行崩溃 3.正常运行
class Test1
{
public:
void Print()
{
cout<<"haha"<<endl;
}
private:
int a_;
};
int main()
{
Test1* p = nullptr;
p->Print();
return 0;
}
练习2: 下列程序的运行结果:1.编译报错 2. 运行崩溃 3.正常运行
class Test2
{
public:
void Print()
{
cout<<a_<<endl;
}
private:
int a_;
};
int main()
{
Test2* p = nullptr;
p->Print();
return 0;
}
答案:
练习1:正常运行
练习2:运行崩溃
可能有人会认为练习1中的p->Print();
这里对空指针解引用了,实则不然。这里不涉及对空指针的解引用,只是一个函数调用而已,因为成员函数的地址不存在对象中,在公共代码区域。这里p为空指针传入print中,然后this接受print地址直接输入。真正的对空指针的解引用在Print
函数内,this接受的是a_,因为a_是成员变量会对print的p进行解引用。
文章首先区分了面向过程与面向对象的编程思想,接着引入了类的定义,讲解了类的成员变量、成员函数以及访问限定符,强调了封装的重要性。通过实例化,读者应了解到类如何描述对象,以及类的空间大小和内存对齐规则。最后我们深入探讨了this指针的特性及其在成员函数中的应用,结合练习题帮助读者巩固理解。后续我会补上类和对象的其他特点。期待你的关注。