C修炼类和对象上
C++修炼:类和对象(上)
Hello大家好!很高兴我们又见面啦!给生活添点passion,开始今天的编程之路!
我的博客:
我的专栏: 、 、 、
欢迎点赞,关注!
1、类的定义
1.1、类的介绍
我们先来定义一个类:
#include<iostream>
using namespace std;
class TEST
{
public:
//成员函数
void test()
{
return;
}
private:
//成员变量
int _a;
int _b;
};
int main()
{
return 0;
}
现在我们来剖析一下这串代码。 class为类的关键字,TEST为类的名字。{}中的为类的主体。
其实 类就是c语言里面结构体的升级版 。但需要注意的是,C++兼容C的定义方式,所以在c++中也可以定义struct结构体。并且在c++中,我们不用typedef了,举个例子:
typedef struct ListNodeC
{
struct ListNodeC* next;
int val;
}LTNode;
这是我们在学习链表是定义的节点。我们传参时都是LTNode* ph传入头结点。但是在 C++ 中,我们 不需要typedef 了,直接拿 listNodeC 就可以代表这个节点。
我们可以把上面的代码写成这样:
struct ListNodeC
{
struct ListNodeC* next;
int val;
}LTNode;
这是结构体的第一点升级,第二点升级就是在 C++中结构体也可以定义成员函数。
在C++中,虽然可以使用struct,但还是推荐使用class定义类。
类里面成员变量的命名大多是_加变量名。 当然了,这个还是要根据公司规定以及个人习惯来调整的。
类中的成员函数默认为内联
1.2、访问限定符
在上面的代码中我们并没有介绍public和private。 其实public和private是访问限定符。在public后面的成员函数和成员变量可以直接在类的外部使用。private后面的成员函数和成员变量不能被直接使用。
通常我们把成员函数定义为public,把成员变量定义为private。
类的正常使用:
#include<iostream>
using namespace std;
class TEST
{
public:
//成员函数
int test(int a,int b)
{
return a+b;
}
private:
//成员变量
int _a;
int _b;
};
int main()
{
TEST A;
int c = 10;int d = 20;
cout << A.test(c, d) << endl;
return 0;
}
1.3、类域
类定义了一个新的作用域,类的所有成员函数都在类的作用域中。在类体外定义成员时,需要使用类域名::来访问成员。
如果不指定类域的话,在定义函数时,程序在全局域找不到函数的声明就会报错。 编译器不会主动去类域中寻找函数定义。
#include<iostream>
using namespace std;
class TEST
{
public:
//成员函数声明
int test(int a, int b);
private:
//成员变量
int _a;
int _b;
};
int TEST::test(int a, int b)
{
return a + b;
}
int main()
{
TEST A;
int c = 10;int d = 20;
cout << A.test(c, d) << endl;
return 0;
}
2、实例化
2.1、实例化概念
用类的类型在物理内存中创建对象的过程,叫做实例化对象。
例如上面代码的TEST A;这一句就是实例化出了对象A。在这一句之前我们只是定义了类,但并没有给类里面任何一个成员函数和成员变量分配空间。 没有实例化出的对象不能存储数据。
也就是说,在定义时int _a根本没有给_a这个变量开空间,这只是声明了_a这个变量。
2.2、对象大小
对象的大小只包含成员变量的大小,成员函数不占内存空间。 打个比方,我现在实例化出了两个类,分别为A,B。A和B的成员变量是不同的,地址是不同的。但如果访问这两个类的成员函数,他们都会链接到一个地址。所以说我们sizeof(类对象)只用统计成员变量占用的空间。
成员变量占用的空间也符合内存对齐规则。之前在C语言结构体部分说过了,这里就不过多赘述了。
3、this指针
#include<iostream>
using namespace std;
class Date
{
public:
// void Init(Date* const this, int year, int month, int day)
void Init(int year, int month, int day)
{
// this->_year = year;
_year = year;
this->_month = month;
this->_day = day;
}
void Print()
{
cout << _year << "/" << _month << "/" << _day << endl;
}
private:
// 这⾥只是声明,没有开空间
int _year;
int _month;
int _day;
};
int main()
{
Date A;
return 0;
}
我们来看这串代码:Init这个函数在调用时正常的语句应该是_year = year;,可是大家想一个问题,编译器是怎么找到我们的成员变量_year的?
其实,成员函数在传参时都有一个类的指针类型的 this指针 。这个this指针编译器不会显示出来,但实际上他是存在的。我们看上边这串代码,如果再函数调用赋值的时候,我们可以手动把this指针加上去,这样其实并不会报错。这就说明这个this指针是真实存在的。
另外需要注意一点,this指针其实存放在栈区,而不是对象里面。
接着我们来看个有趣的题目:
下面程序编译运行结果是()
A、编译报错 B、运行崩溃 C、正常运行
#include<iostream>
using namespace std;
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
#include<iostream>
using namespace std;
class A
{
public:
void Print()
{
cout << "A::Print()" << endl;
cout << _a << endl;
}
private:
int _a;
};
int main()
{
A* p = nullptr;
p->Print();
return 0;
}
这两串代码运行的结果并不相同。 第一个是正常运行,第二个是运行崩溃 。首先我们应该排除的是A选项。因为不管是C语言中还是C++中,解引用空指针并不会编译报错,只会运行崩溃。
我们再来分析问什么 第一个是正常运行,第二个是运行崩溃。
在第一个程序中我们并没有涉及到开空间的问题。还记得前面说过的吗,成员函数不会占用物理内存,只有成员变量会。我们实例出的类是nullptr说明根本就没开空间。但是第一个程序是不需要访问成员变量的,所以不开空间也没有报错。而第二个程序访问了没开空间的成员变量,所以运行崩溃了。
好了,今天的内容就分享到这,我们下期再见!