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;