我在许多使用过的API中都看到了这种奇怪之处:</ p>
typedef type_t *TYPE;
我的观点是,声明TYPE
类型的变量不会说明事实上已经声明了一个指针。
你和我一样认为这会带来很多困惑吗?这是否意味着强制执行封装,还是有其他原因?你认为这是一种不好的做法吗?
答案 0 :(得分:23)
一般来说,这是一种不好的做法。重要的问题是它与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 *);
答案 1 :(得分:6)
一个常见的习语是用_p
为类型后缀,表示它是一个指针,同时仍然保留了指针质量。
如果指向的结构不可公开,则有时只需使用指针类型。这有助于促进数据隐藏。即。
typedef struct hidden_secret_object * object;
void change_object(object foo);
这允许您在不破坏外部代码的情况下更改hidden_secret_object
的结构方式。
答案 2 :(得分:5)
我也不清楚。我也不喜欢完全大写的类型(我试着为#defines保留那些)。
这种方式通过认为它实际上是一种值类型而很容易自欺欺人,而我们讨论的是指针类型。可以使用智能指针完全抽象出指针类型,但这在C中并不常见。
后缀(如前所述)_p,_ptr,Pointer或沿着这些线的任何东西都会产生清晰度;增加打字,这是真的,但会阻止你犯愚蠢的错误(例如使用'。'代替' - &gt;',......)会花费你宝贵的开发时间。
答案 3 :(得分:5)
这取决于你想要达到的目标。对于您提出的问题,没有任何有意义的“是或否”答案。
你说有时它“不清楚是否声明指针”。但在这种使用模式下,这正是重点! 假设是“不清楚”。事实上,该类型恰好是一个混淆的指针,这不关你的事。这是你不需要知道而不应该依赖的东西。
此使用模型的典型示例是标准库中的va_list
类型。在某些实现中,它可能很容易成为指针类型的typedef。但这是你不应该知道或依赖的东西。
另一个例子是Windows API中HWND
类型的定义。它也是指针类型的typedef,但这不关你的事。
*
字符。在这种情况下,typedef(并且将始终是)代表指针类型的事实向用户公开。通常这种用法不是一种好的编程习惯。如果用户想要创建别名以避免每次都输入*
,他们可以自行完成。由于您在OP中已经提到的原因,这种使用模式通常会导致代码混淆。
在Windows API中也可以找到typedef使用不当的示例。像PINT
这样的Typedef名称完全遵循有缺陷的使用模式。
答案 4 :(得分:1)
如果它是指向不完整类型的指针,或者由于任何其他原因不希望用户取消引用它,我认为这是不好的做法。我从未理解FILE*
。
我也不认为这样做是不好的,因为你有几个级别的间接,并且你想在一些不相关的情况下使用它。 typedef char **argarray
或其他什么。
如果用户需要取消引用它,那么在C中,我认为最好保留*
。在C ++中,人们习惯于使用重载operator*
的用户定义类型,例如迭代器。在C中,这是不正常的。
答案 5 :(得分:1)
像'const'这样的存储类限定符对typedef指针的作用与使用'自然'指针的作用不同。虽然这对于'const'来说通常不是一件好事,但它对于特定于编译器的存储类(如“xdata”)非常有用。声明如下:
xdata WOKKA *foo;
将“foo”声明为存储在默认存储类中的指针,指向xdata中的WOKKA。声明:
xdata WOKKA_PTR bar;会将“bar”声明为存储在xdata中的指针,指向WOKKA中指定的任何存储类中的WOKKA。如果库例程需要指向具有特定存储类的东西的指针,那么在指针类型中定义这些存储类可能很有用。
答案 6 :(得分:1)
偶尔会咬我的屁股:
for (vector<typedef_name_that_doesnt_indicate_pointerness_at_all>::iterator it;
it != v.end(); ++it)
{
it->foo(); // should have been written (*it)->foo();
}
唯一可接受的是,如果该类型是真正不透明的,并且不能直接访问,那么。 IOW,如果某人不得不在API之外取消引用它,那么指针不应该隐藏在typedef后面。
答案 7 :(得分:0)
也许一种使其更具体的方法是调用新的指针类型type_ptr
或类似的东西:
typedef type_t* type_ptr;