从结构struct谈到类class(基于C++实现)

深入理解struct

在C语言中,我们通常使用struct来表示不同数据类型的结合。当然我们也可以在struct中定义函数,在C++中,这是允许的但是不提倡使用,因为有一个比它更好使用的复杂数据类型,叫做类(这在稍后做出介绍)。

使用struct的时候有一个问题:在进行一个比较大的项目工程的时候,我们的数据结构的定义和使用可能在不同的文件中,当我们修改了数据结构中的某个成员,那么,使用该数据结构的函数必须修改,而我们并不知道拿下函数使用该种数据结构,这时候我们怎么做?答案很简单,在函数定义的时候,让该函数属于该数据结构,在再次查找修改函数的时候,只要属于该结构的函数就进行修改就好了,代码如下:

声明数据结构:
struct Time{
    int hour;
    int minute;
    int second;
    void set_time(int h, int m, int s); 
    void tick();
    void show();
    void run();
};
定义函数:
void Time::set_time(int h, int m, int s){}
void Time::tick(){}
void Time::show(){}
void Time::run(){}

补充:成员运算符:表示某个变量的成员,‘.’;表示某个类型的成员,“::”;

类的引出

这样定义就万事大吉了吗?我们如果在其他函数中试图访问该结构中的成员变量(如:hour、minute、second)是成功的,这样我们就有可能无意中修改了数据结构中的某个成员变量的值,当其他函数在使用成员变量的时候,就会使用该可能非法的数据,这样我们就会想到只要让这些成员变量变成私有的就好了,这样除了结构中定义的函数外,其他方式都不可以访问到该成员,这就出现了我们C++常使用的一种数据结构——类。我们进行如下修改。

声明数据结构:
class Time{
    int hour;
    int minute;
    int second;
    void set_time(int h, int m, int s); 
    void tick();
    void show();
    void run();
};
定义函数:
void Time::set_time(int h, int m, int s){}
void Time::tick(){}
void Time::show(){}
void Time::run(){}

如果我们就止步于上面的数据结构,我们定义一个上述类的对象,当我们通过对象调用函数的时候,会发现编译不会通过,提示我们“你访问的对象是私有的”。这里就要说明class和struct最大的区别:struct中的所有成员默认是公开的(public),即任意一个函数都可以进行访问;class中的所有成员默认是私有的(private),即除了class中的成员函数之外,其他方式不可以访问。

我们使用class而不是用struct的原因是因为,我们想让成员变量变成私有的,实现基本数据封装。但是对于成员函数的调用接口我们不必要私有,因为我们创建的对象,要通过成员函数完成项目任务,所以我们应让成员函数的接口公开(public),这就是我们常说的封装。接着进行如下更改,让成员函数变量公有的(public)。

声明数据结构:
class Time{
private:
    int hour;
    int minute;
    int second;
public:
    void set_time(int h, int m, int s); 
    void tick();
    void show();
    void run();
};
定义函数:
void Time::set_time(int h, int m, int s){}
void Time::tick(){}
void Time::show(){}
void Time::run(){}

在这里我们要说说class中成员变量的访问权限:

(1)private:私有的。这是class中成员变量默认的访问权限,这种权限的成员变量,只有该类中成员函数可以访问,子类中的成员函数不可以访问,其他方式也不可以访问。(子类是继承中的概念,在讲继承的时候再谈这个话题,先了解就好)。

(2)protected:保护的。该class中的成员函数可以访问,子类中的成员函数也可以访问,但是其他方式不可以访问。

(3)public:公开的。该class中的成员函数可以访问,子类中的成员函数也可以访问,其他方式亦可以访问。

 构造函数和析构函数

对于class中的成员函数,我们可以自己写一个函数进行初始化,比如set函数,将每个成员变量进行初始化赋值。但是每次创建一个新类都要调用该种函数进行成员的初始化,这大大加大了我们创建使用类的代价,为了让我们每次创建一个对象的时候,都可以自动调用某个函数进行成员变量的初始化,我们可以使用类中重要的成员函数——构造函数(构造函数无返回值,并且函数名与类名一致),它可以在创建对象的时候自动调用,根据创建对象时传入的初始化参数进行成员变量的初始化。

构造函数的格式:Class_name(parameters){}

构造函数的初始化有两种方式:

(1)通过在构造函数内部进行赋值进行初始化,常量不能被赋值,只能使用初始化列表。

(2)通过初始化列表进行初始化,如果成员变量是数组或结构不能使用初始化列表进行初始化。

构造函数的重载:有时候我们需要定义多个构造函数,因为我们可能基于不同的需求对成员变量的初始化操作不同,这就是构造函数的重载(函数重载:函数名相同,但是函数参数不同。系统可以根据传入参数的不同来调用不同的函数)。

析构函数:存在构造函数也就会存在析构函数,析构函数就是在该对象即将被释放的时候做收尾动作,析构函数一定没有参数列表,所以析构函数不可以重载。

析构函数规则:~Class_name(){};

class A{
     int n;
     double d;
 public :  A():n(0),d(0.0){   // constructor_init1
         }
         A(int n){        // constructor_init2
             this->n = n; 
             d = 0.0;           
         }
         void show(){
             cout << "n = " << n << ", d = " << d << endl;
         }
         ~A(){
            cout << “~A()” << endl;
         }
};
int main(int ac, char *av[])
{
    A a1;            // use constructor_init1
a1.show();
    A a2(100);        // use constructor_init2
    (*this).show();
    return 0;
}

this指针:this指针是系统自动定义的,用来保存结构变量的地址。当我们创建一个对象的时候,系统就会自动将该对象对应的类的成员保存在this中,我们可以通过this->mem_val或this->mem_func,来访问该成员。在一个成员函数中,可以不用写this,表示默认使用该成员函数所在类的成员变量。

构造函数总结:

(1)构造函数不同写返回值,其函数名与类名一致。

(2)构造函数在每个对象创建的时候都不被自动调用一次。

 构造函数的调用顺序

·全局对象的构造函数在main之前调用。

·静态局部对象的构造函数在整个程序的执行过程中只调用一次。

class A{
   int n;
public:
   A(int n) : n(n){
         cout << "A(" << n << ‘)‘ <<  endl;
    }   
    ~A(){
        cout << "~A(" << n << ‘)‘ << endl;
    }   
};
void func(){
    A a2(2);
    static A a3(3);
}
int main(int ac, char *av[])
{
    A a4(4);
    cout << "first call func : \n";
    func();
    cout << "second call func : \n";
    func();
 
    return 0;
}
A a1(1); 
//==========================
// 1:全局对象
// 2:函数func中的自动对象
// 3:函数func中的静态全局对象
//4:函数main中的自动对象
//===============================
结果显示:
[[email protected] construtor]# ./call_queue 
A(1)  
A(4)
first call func : 
A(2)
A(3)
~A(2)
second call func : 
A(2)
~A(2)
~A(4)
~A(3)
~A(1)
时间: 2024-10-04 03:25:07

从结构struct谈到类class(基于C++实现)的相关文章

浅谈linux内核栈(基于3.16-rc4)

在3.16-rc4内核源码中,内核给每个进程分配的内核栈大小为8KB.这个内核栈被称为异常栈,在进程的内核空间运行时或者执行异常处理程序时,使用的都是异常栈,看下异常栈的代码(include/linux/sched.h): 1 union thread_union { 2 struct thread_info thread_info; 3 unsigned long stack[THREAD_SIZE/sizeof(long)]; 4 }; THREAD_SIZE值为8KB,因此内核为进程的异常

浅谈java类集框架和数据结构(2)

继续上一篇浅谈java类集框架和数据结构(1)的内容 上一篇博文简介了java类集框架几大常见集合框架,这一篇博文主要分析一些接口特性以及性能优化. 一:List接口 List是最常见的数据结构了,主要有最重要的三种实现:ArrayList,Vector,LinkedList,三种List均来自AbstracList的实现,而AbstracList直接实现了List接口,并拓展自AbstractCollection. 在三种实现中,ArrayList和Vector使用了数组实现,可以认为这两个是

C# 结构struct总结

C# 结构struct总结 1.结构是值类型,而且是密封的,不能继承和派生. 2.结构申明: struct StructName { MemberDeclaration } struct Point { public int x ;  //结构中字段初始化是不允许的 pub int y ; } 3.结构具有以下特点 (1)结构与类非常类似,但是结构是值类型,类是引用类型. (2)结构实例化可以不适用new运算符.但是在显示设置数据成员之后,才能调用他们的值. (3)系统已经为结构提供一个隐式构造

OC基础--结构体 枚举做类成员属性

结构体  枚举作类的成员属性: 定义一个学生类 性别 -- 枚举 生日 入学日期  毕业日期  --  结构体 代码示例: 声明文件 Student.h: #import <Foundation/Foundation.h> typedef struct { int year; int month; int day; } Date; typedef enum { kGenderGirl = 0, kGenderBoy = 1, kGenderChunGe = 2 } Gender; @inter

结构struct

1.结构变量 1)定义结构类型 struct student { char *name; int age; int score[3]; }; 2)定义结构变量 struct student stu1, stu2, *ps, stu[30]; 3)结构变量的成员表示 一般结构变量的成员:stu1.name 指向结构变量的指针成员:ps->name 结构数组元素的成员:stu[0].name 4)结构变量可以被赋初值,也可以被赋值 struct student stu1 = {"wang&qu

使用结构struct作为Dictionary&lt;TKey,TValue&gt;的键

我们经常用简单数据类型,比如int作为泛型Dictionary<TKey,TValue>的key,但有时候我们希望自定义数据类型作为Dictionary<TKey,TValue>的key,如何做到? 如果我们想自定义一个struct类型作为key,就必须针对该struct定义一个实现IEqualityComparer<T>接口的比较类,实现该接口的2个方法:Equals()方法和GetHashCode()方法,前者用来比较两个key是否相等,后者用来获取key的哈希值.

详说C#中的结构struct

一.结构和类的区别 1.结构的级别和类一致,写在命名空间下面,可以定义字段.属性.方法.构造方法也可以通过关键字new创建对象. 2.结构中的字段不能赋初始值. 3.无参数的构造函数无论如何C#编译器都会自动生成,所以不能为结构定义一个无参构造函数. 4.在构造函数中,必须给结构体的所有字段赋值. 5.在构造函数中,为属性赋值,不认为是对字段赋值,因为属性不一定是去操作字段. 6.结构是值类型,在传递结构变量的时候,会将结构对象里的每一个字段复制一份拷贝到新的结构变量的字段中. 7.不能定义自动

Node的结构和Chrome十分相似,基于事件驱动的异步架构

事件驱动:触发一个事件然后再调用相关可用的资源来解决这个事件 异步:无需等待被调用函数的返回值,进行下一项调用 I/O:是input/output的缩写,即输入输出端口 首先,Node是一个架构,通过事件驱动来服务I/O. 在Node中,JS可用随心所欲的访问本地文件,可用搭建WebSocket服务器端,可用链接数据库,可用如Web Worders一样玩转多进程 Node的结构和Chrome十分相似,基于事件驱动的异步架构

【个人使用.Net类库】(3)Excel文件操作类(基于NPOI)

Web开发工作中经常要根据业务的需要生成对应的报表.经常采用的方法如下: 将DataTable导出至Excel文件; 读取模板Excel文件; 修改模板Excel文件对应的内容. 因此,便想到封装一个基于NPOI的Excel操作类(至于为什么不用Excel组件,那是因为Excel组件效率低且必须安装Office),所完成的功能大致如上所示,这样平时的报表开发效率就比原来高效很多. 首先是DataTable导出至Excel文件,代码关键部分有注释说明,具体代码如下所示: /// <summary>