服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - C/C++ - C++类与对象的基础知识点详细分析

C++类与对象的基础知识点详细分析

2023-03-02 15:25Ggggggtm C/C++

类和对象是两种以计算机为载体的计算机语言的合称。对象是对客观事物的抽象,类是对对象的抽象。类是一种抽象的数据类型;变量就是可以变化的量,存储在内存中—个可以拥有在某个范围内的可变存储区域

一、什么是类和对象呢

1、类的引入

C 语言结构体中只能定义变量,在 C++ 中,结构体内不仅可以定义变量,也可以定义函数。 比如: 之前在数据结构初阶中,用C 语言方式实现的栈,结构体中只能定义变量 ;现在以 C++ 方式实现, 会发现 struct 中也可以定义函数。

2、类的定义

?
1
2
3
4
class className
{
// 类体:由成员函数和成员变量组成
};  // 一定要注意后面的分号

class为定义类的关键字,ClassName为类的名字,{}中为类的主体,注意类定义结束时后面分号不能省略。

类体中内容称为 类的成员: 类中的 变量 称为 类的属性 或 成员变量 ; 类中的 函数 称为 类的方法 或者 成员函数 。

类的两种定义方式:

声明和定义全部放在类体中,需注意:成员函数如果 在类中定义 ,编译器可能会将其当成 内 联函数 处理。

类声明放在 .h 文件中,成员函数定义放在 .cpp 文件中,注意: 成员函数名前需要加类名 ::。

3、类的访问限定符

C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。

访问限定符说明:

  • public修饰的成员在类外可以直接被访问;
  • protected和private修饰的成员在类外不能直接被访问;
  • 访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止;
  • 如果后面没有访问限定符,作用域就到 } 即类结束;
  • class的默认访问权限为private,struct为public(因为struct要兼容C) 。

4、类对象的储存方式

我们假想:每个对象中成员变量是不同的,但是调用同一份成员函数,如果按照每实例一个对象都给成员变量和成员函数创造一次空间存储,当一 个类创建多个对象时,每个对象中都会保存一份代码,相同代码保存多次,浪费空间。那么如何解决呢?

针对上面的问题,类的存储就变成了:只保存成员变量,成员函数存放在公共的代码段 。那么一个类的大小其实就是:实际就是该类中”成员变量”之和,当然要注意内存对齐,注意空类的大小,空类比较特殊,编译器给了空类一个字节来唯一标识这个类的对象。

5、this指针的特性

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Date
{
public:
 void Init(int year, int month, int day)
 {
     _year = year;
     _month = month;
     _day = day;
 }
 void Print()
 {
     cout <<_year<< "-" <<_month << "-"<< _day <<endl;
 }
private:
     int _year;     // 年
     int _month;    // 月
     int _day;      // 日
};
int main()
{
     Date d1, d2;
     d1.Init(2022,1,11);
     d2.Init(2022, 1, 12);
     d1.Print();
     d2.Print();
     return 0;
}

我们知道了成员函数是放在了公共代码段。函数体中没有关于不同对象的区分。那么在上面的代码中d1和d2同时掉用了Print()函数,怎么是分别打印出d1对象中的成员变量和d2对象中的成员变量呢?(当然Init函数与Print函数的区分类似)。

C++ 中通过引入 this 指针解决该问题,即: C++ 编译器给每个 “ 非静态的成员函数 “ 增加了一个隐藏 的指针参数,让该指针指向当前对象 ( 函数运行时调用该函数的对象 ) ,在函数体中所有 “ 成员变量 ” 的操作,都是通过该指针去访问。只不过所有的操作对用户是透明的,即用户不需要来传递,编 译器自动完成 。如下图:

C++类与对象的基础知识点详细分析

this指针的特性:

  • this 指针的类型:类类型 * const ,即成员函数中,不能给 this 指针赋值;
  • this 指针是 “ 成员函数 ” 第一个隐含的指针形参,一般情况由编译器通过 ecx 寄存器自动传递,不需要用户传递;
  • this 指针本质上是 “ 成员函数 ” 的形参 ,当对象调用成员函数时,将对象地址作为实参传递给this形参。所以 对象中不存储 this 指针;
  • 只能在 “ 成员函数 ” 的內部使用。

二、类的六个默认成员函数详解

C++类与对象的基础知识点详细分析

什么是默认成员函数呢?

默认成员函数:用户没有显式实现,编译器会生成的成员函数称为默认成员函数。

如果一个类中什么成员都没有,简称为空类。 空类中真的什么都没有吗?并不是,任何类在什么都不写时,编译器会自动生成以下6 个默认成员函数: 

  • 构造函数;
  • 析构函数;
  • 拷贝构造;
  • 赋值重载;
  • 普通对象取地址;
  • const对象取地址。

我们来看一下各个默认的成员函数的概念及实现。本篇我们先掌握构造函数和析构函数,这两个时相对较为麻烦和重要的,下篇我们会接着是西安剩余的默认成员函数以及类和对象剩余的重要的部分。

1、构造函数

构造函数是一个特殊的成员函数,名字与类名相同,创建类类型对象时由编译器自动调用,以保证每个数据成员都有 一个合适的初始值,并且在对象整个生命周期内只调用一次。

构造函数是特殊的成员函数,需要注意的是,构造函数虽然名称叫构造,但是构造函数的主要任务并不是开空间创建对象,而是初始化对象。其特征如下:

  • 函数名与类名相同;
  • 无返回值;
  • 对象实例化时自动调用对应的构造函数;
  • 构造函数可以重载。
  • 如果如果类中没有显式定义构造函数,则C++编译器会自动生成一个默认的无参构造函数,一但用户显式定义编译器将不再生成。

我们结合着以下代码一起理解以下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Date
{
 public:
     // 1.无参构造函数
     Date()
    {}
     // 2.带参构造函数
     Date(int year, int month, int day)
    {
         _year = year;
         _month = month;
         _day = day;
    }
 private:
     int _year;
     int _month;
     int _day;
};
 void TestDate()
{
     Date d1; // 调用无参构造函数
     Date d2(2015, 1, 1); // 调用带参的构造函数
     // 注意:如果通过无参构造函数创建对象时,对象后面不用跟括号,否则就成了函数声明
     // 以下代码的函数:声明了d3函数,该函数无参,返回一个日期类型的对象
     // warning C4930: “Date d3(void)”: 未调用原型函数(是否是有意用变量定义的?)
     Date d3();
}

关于编译器生成的默认成员函数,很多人都会有疑惑:不实现构造函数的情况下,编译器会 生成默认的构造函数。但是看起来默认构造函数又没什么用? 象调用了编译器生成的默 认构造函数,但是 对象中的成员函数 依旧是随机值。也就说在这里 编译器生成的 默认构造函数并没有什么用??

C++ 把类型分成内置类型 ( 基本类型 ) 和自定义类型。内置类型就是语言提供的数据类 型,如: int/char... ,自定义类型就是我们使用 class/struct/union等自己定义的类型。其实编译器生成默认的构造函数会对自定类型成员 调用的它的默认成员函数,并不会对内置类型的成员变量进行初始化。

如下面代码:Data中并没有自己实现构造函数,系统会自动生成。让后会对自定义类型成员_t调用它的默认成员函数。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Time
{
public:
 Time()
 {
     cout << "Time()" << endl;
     _hour = 0;
     _minute = 0;
     _second = 0;
 }
private:
     int _hour;
     int _minute;
     int _second;
};
class Date
{
private:
     // 基本类型(内置类型)
     int _year;
     int _month;
     int _day;
     // 自定义类型
     Time _t;
};
int main()
{
     Date d;
     return 0;
}

无参的构造函数和全缺省的构造函数都称为默认构造函数,并且默认构造函数只能有一个。 注意:无参构造函数、全缺省构造函数、我们没写编译器默认生成的构造函数,都可以认为 是默认构造函数。

2、析构函数

通过前面构造函数的学习,我们知道一个对象是怎么来的,那一个对象又是怎么没呢的?这里就用到了析构函数。 析构函数:与构造函数功能相反,析构函数不是完成对对象本身的销毁,局部对象销毁工作是由编译器完成的。而对象在销毁时会自动调用析构函数,完成对象中资源的清理工作。

析构函数 是特殊的成员函数,其特征如下:

  • 析构函数名是在类名前加上字符 ~。
  • 无参数无返回值类型;
  • 一个类只能有一个析构函数。若未显式定义,系统会自动生成默认的析构函数。注意:析构函数不能重载;
  • 对象生命周期结束时, C++ 编译系统系统自动调用析构函数。

我们结合下面代码一起理解一下。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
typedef int DataType;
class Stack
{
public:
 Stack(size_t capacity = 3)
 {
     _array = (DataType*)malloc(sizeof(DataType) * capacity);
     if (NULL == _array)
     {
         perror("malloc申请空间失败!!!");
         return;
     }
     _capacity = capacity;
     _size = 0;
}
void Push(DataType data)
{
     //CheckCapacity();
     _array[_size] = data;
     _size++;
}
 // 其他方法...
 ~Stack()
 {
     if (_array)
     {
         free(_array);
         _array = NULL;
         _capacity = 0;
         _size = 0;
     }
}
private:
     DataType* _array;
     int _capacity;
     int _size;
};
void TestStack()
{
     Stack s;
     s.Push(1);
     s.Push(2);
}

关于编译器自动生成的析构函数,是否会完成一些事情呢?编译器生成的默认析构函数,对自定类型成员调用它的析构函数。 注意:创建那个类的对象则调用该类的析构函数,销毁哪个类的对象则调用该类的析构函数。 如果类中没有申请资源时,析构函数可以不写,直接使用编译器生成的默认析构函数, 有资源申请时,一定要写,否则会造成资源泄漏。 今天的内容就到这里,需要重点理解和掌握构造函数和析构函数,后续我会更新下篇带大家理解完类和对象ovo!

到此这篇关于C++类与对象的知识点详细分析的文章就介绍到这了,更多相关C++类与对象内容请搜索服务器之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持服务器之家!

原文链接:https://blog.csdn.net/weixin_67596609/article/details/128437303

延伸 · 阅读

精彩推荐
  • C/C++关于C++中void*的小作用浅析

    关于C++中void*的小作用浅析

    这篇文章主要给大家介绍了关于C++中void*的一些小作用,文中通过示例代码介绍的非常详细,对大家学习或者使用C++具有一定的参考学习价值,需要的朋友...

    sinolzeng8092021-05-31
  • C/C++C++详细讲解函数调用与Struct和CLass的区别

    C++详细讲解函数调用与Struct和CLass的区别

    主调函数使用被调函数的功能,称为函数调用。在C语言/C++中,只有在函数调用时,函数体中定义的功能才会被执行,下面让我们详细来了解...

    丶布布6232022-11-27
  • C/C++C语言 条件判断详细介绍

    C语言 条件判断详细介绍

    本文主要讲解C语言 条件判断,这里整理了相关资料,详细说明了判断语句知识要点,希望能帮助学习C语言的同学...

    C语言教程网4642021-04-13
  • C/C++纯C语言:贪心Prim算法生成树问题源码分享

    纯C语言:贪心Prim算法生成树问题源码分享

    这篇文章主要介绍了贪心Prim算法生成树问题源码,有需要的朋友可以参考一下...

    C语言教程网6042021-01-13
  • C/C++Linux c中define的用法小结

    Linux c中define的用法小结

    学习了这么多年C语言,说实话对宏自以为了如指掌了,没想到看内核代码的时候还是那么吃力,设备驱动代码中有很多这样或者那样的宏定义,各种defin...

    C语言教程网8682021-03-22
  • C/C++C语言递归实现归并排序详解

    C语言递归实现归并排序详解

    这篇文章主要为大家详细介绍了C语言递归实现归并排序,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下, 希望能...

    Icy Hunter7772022-10-11
  • C/C++C/C++函数的调用约定的使用

    C/C++函数的调用约定的使用

    本文主要介绍了C/C++函数的调用约定的使用,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着...

    dvlinker5852023-02-08
  • C/C++C语言内存的动态分配比较malloc和realloc的区别

    C语言内存的动态分配比较malloc和realloc的区别

    这篇文章主要介绍了C语言内存的动态分配比较malloc和realloc的区别,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是本文的详细内容,需要的...

    唐世光8792021-11-22