爱技术 & 爱分享
爱蛋蛋 & 爱生活

struct与class之间的异同

好吧,这篇文章主要讲一下 C++中 struct 和 class 之间的情感纠葛


什么时候应该使用struct取代class?


答案是: 当你觉得开心的时候….

好吧, 看到这个答案各位看官肯定心里不由自主的来了一句 “MMP,你TM确定不是在逗我?”

诶~ 诶~ 诶~ 诶~ ,别打我~ 别打我~ ,先停一下。

虽然这个答案并没有任何技术水平,不过这的确指出了一个重要的特性 :关键词 struct 本身并不一定象征其后声明的任何东西。

我们完全可以使用struct 来替代class,struct 任然可以声明 public 、protect、private等等存取级别与一个完全 public 的接口,以及 virtual function 和单一继承,多重继承,虚拟继承…

不过大多数教科书中说到 struct 的时候,大多数意思都是指一个数据集合体,没有 private data ,也没有相应的操作(member function)。

也就是纯 C 的用法。这种用法应该跟 C++ 的 自定义类型(user_defined_type)用法区别开来。

在C所支持的 struct 和 C++所支持的 class 之间,有一个观念上的重要差异。

我的重点很简单:关键词本身并不提供这种差异。也就是说,下面这个段代码,可以说 “卧槽,这是一个 class” ,也可以说 “哎哟,这是一个struct”。

//struct 名称(或class 名称)暂时省略
{
 public:
    operator int();
    virtual void foo();
    //···
 protected:
    static int object_count;
    //mumble
}

事实上你可以说他是个struct,也可以说他是个class。这两种声明观念上的意义取决对“声明”本身(declarationbody)的检验(又是一句废话~ ~ ~ ~ ~)。

再来看一个例子:

#include <iostream>
using namespace std;
struct A;
class B
{
public:
    B();
    ~B();
    A *a;
    void Test();
};
class A
{
    int x;
public:
    A(){};
    ~A(){};
    int y;
};


B::B()
{
    a = new A();
}

B::~B()
{
    delete a;
}

void B::Test()
{
    cout << a->y << endl;
}

大家不妨猜猜看这个代码会不会有问题?

答案:不会有问题!

大家会很奇怪,为什么声明使用的是class 前置声明却使用的是struct 而且还不会有问题…

事实上这在C++中是允许的,完全能成功编译,只不过编译器会报一个不一致的警告罢了:

warning C4099: “A”: 类型名称以前使用“struct”现在使用的是“class”
以上为Visual Studio 2017 测试结果↑

真正的问题并不在于所有“用户自定义类型”的声明是否必须使用相同的关键字,而在于使用class或struct关键字是否可以给予“类型的内部声明”以某种承诺。

也就是说,如果struct关键字的使用实现了C的数据抽象观念,而class关键字实现的是C++的ADT(Abstract Data Type)观念,那么当然“不一致”是一种错误的语法,就好像接下来你将看到的错误,一个object被矛盾的声明为static和extern:

//不合法? 是的
//以下两个声明造成矛盾的存储空间
static int foo;
·······
extern int foo;

这组声明对于foo的存储空间造成矛盾。但是struct 和 class 这两个关键词并不会造成这样的矛盾。一个类型是struct还是class是由声明本身(declarationbody)决定的,“一致性的用法”只不过是一种风格上的问题而已。

那么再看一种情况:

template <struct T>
struct MyStruct
{
    T x;
};

这样子合法?

答案肯定是否。

要这样子:

template <class T>
struct MyStruct
{
    T x;
};

和这样子:

template <typename T>
struct MyStruct
{
    T x;
};

才是OK的。

好奇的你一定在问为什么呢,不是我开心就好,难道不是我想怎么用就怎么用?

老铁,这次真的不是你开心就好了。因为C++保留struct本身就是为了与C 兼容,而C中并没有 templates 这个东西,那么也就没有必要在模板中支持strcut声明模板形参。

看到现在大家是不是觉得C++怎么这么多事,如果这个语言只支持一个关键字,那得省掉多少混淆和迷惑啊。

But body you need to know ,如果C++需要支持现存的C程序代码,它就不能不支持struct 。

好的,找茬的又来了。那你就不能不引入class 而只使用 struct ?

额 这个 我也不知道搞出C++的那群人怎么想的 。 而事实上引入它的确非常的令人满意嘛(are you kidding me?),因为这个语言引入的不只是关键字,还有它所支持的封装和继承的则哲学。

啊,苍天啊,估计又让你回忆起了 C++ 老师在你耳边大声讲的万物皆类的思想…

估计大家还想起一个问题:柔性数组

这是C程序员的一种神奇技巧,在C中把一个元素个数为0(当然你想设成,1、2、3····也不是不可以,只不过需要自己代码中使用的时候相应的改变一下)的数组放在struct的末尾(只能是末尾)。

例如下面的代码就实现了一个大小可变的数组:

#include<stdio.h>
#include<malloc.h>

typedef struct _SoftArray{
    int len;
    int array[];
}SoftArray;

int main()
{
    int len = 10;

    SoftArray *p=(SoftArray*)malloc(sizeof(SoftArray) + sizeof(int)*len);
    //······
    free(p);
    return 0;
}

但是如果我们改用class来声明,而该class具有一下条件之N:

  • 指定多个access section,内含数据
  • 从另一个class派生而来
  • 定义了一个以上virtual functions

那么或许可以顺利转化,或许不行。

原因如下:

  • C++中凡是处于同一个access section 的数据,必定保证以其声明的顺序出现在内存布局之中,然而被放置在多个access section 中的数据,排列顺序就不一定了。所以以下代码只有当按照public,protect,private的顺序存放数据的时候才能使用柔性数组(But 这一点我们是不能保证的,C++标准中没有强制规定,即按各厂商自己的实现方式来)
class Test
{
 public:
 //·····
 protect:
 //·····
 private
 //·····
    char array[];
}
  • 同理,base class 和 dived class 的data members的布局也未规定谁先谁后,因而也不能保证柔性数组一定有效。
  • virtual function的存在也使柔性数组是否可行成为一个问号。

所以最好的忠告就是,不要在C++风格的class/struct中(当然C风格的struct是完全没问题的)使用柔性数组,除非你真的很清楚你所使用的编译器会怎么实现。

总结一下:C++中struct跟class的区别主要有以下几点。

  • struct 中默认访问权限是 public的,而class 是private的。
  • 模板声明不能使用struct
  • 柔性数组C风格struct 支持, C++风格是否支持随编译器实现而定

到底是用struct还是class,完全看个人的喜好,你可以将程序里所有的class全部替换成struct,它依旧可以很正常的运行。

但我给出的最好建议,还是:当你觉得你要做的更像是一种数据结构的话,那么用struct,如果你要做的更像是一种对象的话,那么用class。

当然,我在这里还要强调一点的就是,对于访问控制,应该在程序里明确的指出,而不是依靠默认,这是一个良好的习惯,也让你的代码更具可读性。

赞(0) 传送门
本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。编程日志 » struct与class之间的异同
分享到: 更多 (0)

游戏 && 后端

传送门传送门