欢迎来到Cefler的博客
博客主页:那个传说中的man的主页
个人专栏:题目解析
推荐文章:题目大解析2
在c语言当中,我们会注重过程,人是怎么将衣服放入洗衣机的,洗衣机的内部是如何操作运行的。
C++是基于面向对象的,关注的是对象
在c语言中,结构体中只能定义变量,但是在c++中,结构体不但可以定义变量,还可以定义函数
struct Stack
{
void Init()
{
a = NULL;
top = capacity = 0;
}
int* a;
int top;
int capacity;
};
int main()
{
Stack s1;//c++中可以忽略struct
s1.Init();
return 0;
}
语法
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
类中的变量叫做类的属性或成员变量;类中的函数叫做类的方法或成员函数;类中的内容称为类的成员
☂️1.类的声明和定义全部都在类体中进行
若函数的定义也在类体中进行,编译器可能会将其当作内联函数处理
☂️2.类的声明和定义分开
若类的声明在.h文件中进行,类的函数定义在.cpp文件中进行,成员函数前需加上
类名::
一般声明类的变量我们会在变量前/后加上_,m;
原因是为了说明其局部性。
class Date
{
void Init(int year, int month, int day)
{
year = year;
}
int year;
int month;
int day;
};
如上述代码,编译器并不会报错,但这里问题出在函数的参数和类的成员变量的命名起冲突了,而一般在类中会先搜索类内的局部变量,所以这里是类中的year自己赋值给本身,并无意义。
所以我们才出现了这一套的命名规则。
class Date
{
void Init(int year, int month, int day)
{
year = year;
}
int year;
int month;
int day;
};
int main()
{
Date d1;
cout << sizeof(Date) << endl;
cout << sizeof(d1) << endl;
return 0;
}
上述代码中,结果是多少呢?
如果只看类中的成员变量,我们知道有3个int,所以是12个字节。
但是函数在类中占不占空间呢?
我们先看下结果
由此观之,函数在类中是不占空间的,成员函数存放在公共的代码段。
事实上,类中的方法即函数共用内存的中的一块区域。
可以这样理解,当我们实例化多个类的对象时,类的对象就是一个房子,房子中有浴室、卧室、厨房,而每个对象的成员变量就是相当这些浴室、卧室、厨房。
所以成员变量是相对独立的,每实例化一个对象,都要在内存中开辟空间。
但是函数不一样,函数相当于小区里的篮球场,便利店,是共有的。
它既属于每个对象,又不单单只属于某个对象。所以它只是在内存中占用一个区域。
但如上的代码结果中,为什么显示所占内存空间为1呢?
实际上,一个空类,编译器会给一个字节的空间证明该类存在过,它不存储数据,但占位
我们不可以用类名.成员变量去访问类的成员。
因为类是一个声明,它不开辟空间,自然就不能访问
我们只有实例化出一个对象(开辟空间)后,才能通过对象名.成员变量访问
#include
using namespace std;
class Fun1
{
public:
void Print()
{
cout << "Hello world" << endl;
}
};
int main()
{
Fun1 f1,f2;
f1.Print();
f2.Print();
return 0;
}
如上代码中,我们创建了f1,f2两个对象,并且两个对象都一起调用了类中的Print函数,但是我们可以看到,在类中并没有对不同对象的区分,那么在调用类中的函数或成员变量时,编译器是怎么区分f1和f2这两个对象呢?
而这里,其实编译器用this关键字做了一个隐形加工,如果还原原貌,上述代码应为这样
class Fun1
{
public:
void Print(Fun1* const this)
{
cout << "Hello world" << endl;
}
};
int main()
{
Fun1 f1,f2;
f1.Print(&f1);
f2.Print(&f2);
return 0;
}
我们了解以下this关键字的特性⭐️
this指针存在哪?答:this是形参,所以存储在栈区上
this指针可以为空吗?答:可以。
// 1.下面程序编译运行结果是? A、编译报错 B、运行崩溃 C、正常运行
class A
{
public:
void Print()
{
cout << "Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
有人可能会认为选B,因为这里进行了空指针的引用。
但实际上,编译器在这里调用Print函数时,并没有对空指针进行引用。
我们在上述中知道,成员函数是存储在公共代码区中的,并没有存储在类当中,所以我们调用成员函数时,并不根据类的地址,而是成员函数名,所以,类即使是非空或空指针都没区别。
所以这里应该是正常运行,选C
class A
{
public:
void PrintA()
{
cout<<_a<<endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->PrintA();
return 0;
}
在PrintA中cout<<_a<
而这里this是空指针,引用空指针会导致运行崩溃,选B。
1.c语言实现
#include
#include
using namespace std;
typedef int DataType;
struct Stack
{
DataType* a;
int top;
int capacity;
};
void InitStack(Stack* st)
{
assert(st);
st->a = NULL;
st->top = 0;
st->capacity = 0;
}
void PushStack(Stack* st, int x)
{
assert(st);
//判断是否需要扩容
if (st->top == st->capacity)
{
int newcapacity = st->top == 0 ? 4 : st->capacity * 2;
int* tmp = (int*)realloc(st->a,sizeof(int) * newcapacity);
if (nullptr == tmp)
{
perror("realloc fail");
exit(-1);
}
st->a = tmp;
st->capacity = newcapacity;
}
st->a[st->top] = x;
st->top++;
}
bool StackEmpty(Stack* st)
{
assert(st);
return st->top == 0;
}
void PopStack(Stack* st)
{
assert(st);
assert(!StackEmpty(st));
st->top--;
}
可以看到,在用C语言实现时,Stack相关操作函数有以下共性:
2.c++实现
#include
#include
using namespace std;
typedef int DataType;
class _Stack
{
public:
void Init()
{
a = NULL;
top = 0;
capacity = 0;
}
void Push(DataType x)
{
if (top == capacity)
{
int newcapacity = top == 0 ? 4 : capacity * 2;
DataType* tmp = (DataType*)realloc(a, sizeof(DataType) * newcapacity);
if (tmp == NULL)
{
perror("realloc fail");
exit(-1);
}
a = tmp;
capacity = newcapacity;
}
a[top++] = x;
}
bool Empty()
{
return top == 0;
}
void Pop()
{
assert(!Empty());
top--;
}
private:
DataType* a;
int top;
int capacity;
};
C++中通过类可以将数据 以及 操作数据的方法进行完美结合,通过访问权限可以控制那些方法在类外可以被调用,即封装,在使用时就像使用自己的成员一样,更符合人类对一件事物的认知。
而且每个方法不需要传递Stack*的参数了,编译器编译之后该参数会自动还原,即C++中 Stack * 参数是编译器维护的,C语言中需用用户自己维护。
如上便是本期的所有内容了,如果喜欢并觉得有帮助的话,希望可以博个点赞+收藏+关注❤️ ,学海无涯苦作舟,愿与君一起共勉成长