typedef的指针类型被认为是不好的做法吗?

可能重复:
Typedef指针好主意?

我在许多使用过的API中都看到了这种奇怪之处:

typedef type_t *TYPE; 

我的观点是声明一个TYPE的变量并不能说明实际上是一个指针被声明了。

你和我一样认为这会带来很多困惑吗? 这是否意味着强制执行封装,还是有其他原因? 你认为这是一种不好的做法吗?

一般来说,这是一种不好的做法。 重要的问题是它与const不兼容:

 typedef type_t *TYPE; extern void set_type(TYPE t); void foo(const TYPE mytype) { set_type(mytype); // Error expected, but in fact compiles } 

为了让foo()的作者表达他们的真正含义,提供TYPE的库也必须提供CONST_TYPE

 typedef const type_t *CONST_TYPE; 

所以foo()可以有签名void foo(CONST_TYPE mytype) ,此时我们已经陷入了闹剧。

因此经验法则:

创建结构的typedef(特别是不完整的结构),而不是指向那些结构的指针。

如果底层结构的定义不是公开可用的(这通常是值得称道的),那么该封装应该由结构不完整提供,而不是由不方便的typedef提供:

 struct type_t; typedef struct type_t type_t; void set_type(type_t *); int get_type_field(const type_t *); 

一个常见的习语是用_p后缀这个类型来表示它是一个指针,同时仍然保留了指针质量。

有时,如果指向的结构不可公开,则只需使用指针类型。 这有助于促进数据隐藏。 即

 typedef struct hidden_secret_object * object; void change_object(object foo); 

这允许您在不破坏外部代码的情况下更改hidden_secret_object的结构方式。

我也不清楚。 我也不喜欢完全大写的类型(我试着为#defines保留那些)。

通过这种方式可以轻松地认为它实际上是一种值类型,而我们正在讨论指针类型。 可以使用智能指针完全抽象出指针类型,但这在C语言中并不常见。

后缀(如前所述)_p,_ptr,Pointer或沿着这些线的任何东西都会产生清晰度; 增加打字,这是真的,但会阻止你犯愚蠢的错误(例如使用’。’而不是’ – >’,……)会花费你宝贵的开发时间。

这取决于你想要达到的目标。 对于您提出的问题,您的问题没有任何有意义的“是或否”答案。

  • 如果你试图创建一个抽象句柄类型,暗示用户不应该知道或关心隐藏在类型后面的东西,那么键入定义指针类型就完全没问了。 总的来说,今天它可能是一个指针类型,明天它可能会变成一个整数类型,后来它可能变成其他东西。 这正是指针类型typedef在大多数库接口中通常使用的内容。

你说有时它“不清楚是否声明了指针”。 但在这种使用模式下,这正是重点! 它应该是“不清楚”。 事实上,该类型恰好是一个混淆的指针,这不关你的事。 这是你不需要知道而不应该依赖的东西。

此使用模型的典型示例是标准库中的va_list类型。 在某些实现中,它可能很容易成为指针类型的typedef。 但这是你不应该知道或依赖的东西。

另一个例子是Windows API中HWND类型的定义。 它也是指针类型的typedef,但这不关你的事。

  • 一种完全不同的情况是当你将指针类型作为速记forms输入时,只需要使声明更短,而不必每次都输入*字符。 在这种情况下,typedef(并且将始终是)代表指针类型的事实向用户公开。 通常这种用法不是一种好的编程习惯。 如果用户想要创建别名以避免每次都输入* ,他们可以自己完成。

由于您在OP中已经提到的原因,此使用模型通常会导致代码更加混淆。

在Windows API中也可以找到typedef使用不当的示例。 像PINT这样的Typedef名称完全遵循有缺陷的使用模型。

如果它是指向不完整类型的指针,或者由于任何其他原因不希望用户取消引用它,我认为这不是一种不好的做法。 我从来不理解FILE*

我也不认为这样做是不好的,因为你有几个级别的间接,并且你想在一些不相关的情况下使用它。 typedef char **argarray ,或者别的什么。

如果用户需要取消引用它然后在C中,我认为最好保留* 。 在C ++中,人们习惯于使用重载operator*用户定义类型,例如迭代器。 在C中,这是不正常的。

像’const’这样的存储类限定符对typedef指针的作用与使用’natural’指针的作用不同。 虽然这对于’const’来说通常不是一件好事,但它对于特定于编译器的存储类(如“xdata”)非常有用。 声明如下:

 xdata WOKKA * foo;

将“foo”声明为存储在默认存储类中的指针,指向xdata中的WOKKA。 声明:

 xdata WOKKA_PTR吧;

将“bar”声明为存储在xdata中的指针,指向WOKKA中指定的任何存储类中的WOKKA。 如果库例程需要指向具有特定存储类的事物的指针,则在指针类型中定义这些存储类可能很有用。

偶尔咬我的屁股:

 for (vector::iterator it; it != v.end(); ++it) { it->foo(); // should have been written (*it)->foo(); } 

唯一可以接受的是,如果该类型是真正不透明的,根本不能直接访问。 IOW,如果某人不得不在API之外取消引用它,那么指针不应该隐藏在typedef之后。

也许一种使其更具体的方法是调用新的指针类型type_ptr或类似的东西:

  typedef type_t* type_ptr;