翻译《有关编程、重构及其他的终极问题?》——19.如何合理的从一个构造函数中调用另外一个构造函数

本文探讨了在C++中合理地从一个构造函数中调用另一个构造函数的方法。介绍了几种常见的实现方式及其优缺点,并重点讲解了C++11引入的代理构造函数特性。

翻译《有关编程、重构及其他的终极问题?》——19.如何合理的从一个构造函数中调用另外一个构造函数

标签(空格分隔): 翻译 技术 C/C++
作者:Andrey Karpov
翻译者:顾笑群 - Rafael Gu
最后更新:2017年02月22日


19.如何合理的从一个构造函数中调用另外一个构造函数

这个问题是从LibreOffice项目中发现的。PVS-Studio诊断的错误描述为:V603 The object was created but it is not being used. If you wish to call constructor, ‘this->Guess::Guess(….)’ should be used(译者注:大意为有个创建了但没有被使用的对象,如果想调用构造函数,应该使用‘this->Guess::Guess(….)’这样)。

Guess::Guess()
{
  language_str = DEFAULT_LANGUAGE;
  country_str = DEFAULT_COUNTRY;
  encoding_str = DEFAULT_ENCODING;
}

Guess::Guess(const char * guess_str)
{
  Guess();
  ....
}

解释
好的程序员讨厌写模棱两可的代码,很棒。但在处理多个构造函数时,想尽量让代码简单和干净,就有些搬起石头砸自己的脚了。

你知道的,一个构造函数式不能被像普通函数那样调用的。如果我们写了“A::A(int x) { A(); }”这样的代码,运行时就会导致创建一个A类型的临时匿名对象,而不是调用那个没有参数的构造函数。

所以上面问题代码运行后,实际发生的事情是这样:一个Guess类型的临时匿名对象被创建出来后,马上就被销毁,导致其language_str等成员变量未初始化。

正确的代码
通常有三种方式在构造函数中避免模棱两可的代码。让我们看下把。

第一种方式是实现一个专门用来初始化的函数,然后在所有的构造函数中调用它。因为这个方式显而易见,我这里就不写示例代码了。

这种方式是一种不错的、可靠的、干净的并且安全的技术。然而一些差劲的程序员企图让他们的代码看起来更短。所以我不得不再说说另外两种方式。

这另外的两种方式是非常危险的,并且需要你充分理解它们是如何工作的,而且你要知道你必须面对的后果是什么。

第二种方式:

Guess::Guess(const char * guess_str)
{
  new (this) Guess();
  ....
}

第三种方式:

Guess::Guess(const char * guess_str)
{
  this->Guess();
  ....
}

这第二种以及第三种方式是相当危险的,因为类实例化了两次。这种代码会引起一些微妙的bug,所以坏处会多于好处。大家可以考虑一下类似这种构造函数在那些情况下是合适的,那些情况下不太合适。

这里有一个例子是合适的情况:

class SomeClass
{
  int x, y;
public:
  SomeClass() { new (this) SomeClass(0,0); }
  SomeClass(int xx, int yy) : x(xx), y(yy) {}
};

上面这段代码是很安全的,因为其类中占有简单的数据类型,而且没有任何基类。所以两次的构造函数调用不会有任何危险。

然后,下面�有另外一段代码,当调用构造函数时会引起错误:

class Base 
{ 
public: 
 char *ptr; 
 std::vector vect; 
 Base() { ptr = new char[1000]; } 
 ~Base() { delete [] ptr; } 
}; 

class Derived : Base 
{ 
  Derived(Foo foo) { } 
  Derived(Bar bar) { 
     new (this) Derived(bar.foo); 
  }
  Derived(Bar bar, int) { 
     this->Derived(bar.foo); 
  }
}

我们使用了语句“new (this)Derived(bar.foo);”或者“this->Derived(bar.foo)”来调用构造函数。

基类的对象已经被创建了,而且其成员变量也被初始化了。再次调用构造函数会导致初始化两次。结果指针ptr被再次新分配的内存地址给替换了,导致内存泄露。至于对std::vector类型的两次初始化,其结果就更难预测了。只有一件事情是肯定的:像这样的代码不应被允许。

难道我们要处理这么多头疼的事情才能解决这个问题吗?如果你不使用C++11的特性,那么就是用第一种方式吧(创建一个专门的初始化函数)。其实,一个明确的调用构造函数的需求是很少见的。

建议
现在我们还有最后一个特性要介绍,用来帮助我们解决调用构造函数的问题!

C++11允许我们的构造函数调用其他构造函数(被称之为代理)。这种方式可以让构造函数用最少的代码使用其他构造函数。

比如:

Guess::Guess(const char * guess_str) : Guess()
{
  ....
}

要像学习更多有关代理构造函数的知识,请看如下链接:
1. Wikipedia,C++11:对象构造函数的改进
2. C++11 FAQ:代理构造函数
3. MSDN:统一初始化和代理构造函数

<think>在C语言中,static函数的作用域被限制在定义它的文件内部,这意味着它不能被其他文件直接调用。这是由static关键字的语义决定的:它使函数具有内部链接属性,仅在当前翻译单元可见[^2][^3]。 然而,如果确实需要跨文件调用static函数,可以通过以下方法间接实现: ### 方法:使用函数指针传递 1. **在定义static函数的文件中**: - 定义一个非静态的函数(或全局函数指针),用于返回指向该static函数的指针。 - 示例: ```c // file1.c static void hidden_func(void) { // 静态函数的实现 } // 提供获取该函数指针的接口 void (*get_hidden_func(void))(void) { return hidden_func; } ``` 2. **在调用文件中**: - 声明获取函数指针的接口,然后通过函数指针间接调用static函数。 - 示例: ```c // file2.c // 声明获取函数指针的函数 void (*get_hidden_func(void))(void); int main() { void (*func_ptr)(void) = get_hidden_func(); func_ptr(); // 间接调用file1.c中的static函数 return 0; } ``` ### 注意事项 - **设计考量**:此方法绕过了static的限制,但破坏了static的封装性。通常static用于隐藏不需要暴露的函数,因此应谨慎评估是否真的需要跨文件调用。 - **替代方案**:如果函数需要跨文件使用,应将其声明为非static(即默认的extern链接属性)。 - **线程安全**:如果涉及多线程,需注意函数指针传递的同步问题。 ### 总结 虽然技术上可以通过函数指针间接调用其他文件中的static函数,但这违背了static的设计初衷。建议重新设计代码结构,将需要共享的函数改为非静态全局函数。 §§相关问题§§ 1. C语言中static关键字对变量和函数的作用有何不同? 2. 如何理解C语言中的内部链接和外部链接? 3. 函数指针在C语言中有哪些常见应用场景?</think>### 在C语言中调用其他文件的static函数的方法 在C语言中,`static`函数的设计目的就是**限制其作用域仅在定义它的文件内部**,这是C语言的封装机制[^2][^3]。因此,**无法直接在其他文件中调用static函数**,这是语言特性的限制而非技术缺陷。 #### 为什么不能直接调用? 1. **作用域限制**:`static`关键字使函数具有内部链接属性,编译器不会将其名称导出到目标文件的外部符号表[^3] 2. **封装性**:设计初衷是隐藏实现细节,避免命名冲突(不同文件可定义同名static函数)[^2] 3. **存储方式**:虽然存储在静态区,但访问权限仅限于本文件[^3] --- ### 间接调用方案(通过函数指针) 若确实需要跨文件访问,可通过函数指针间接实现: **步骤:** 1. **在定义static函数的文件中**(file1.c): ```c // 声明函数指针类型 typedef void (*FuncPtr)(void); static void hidden_func() { printf("静态函数被调用\n"); } // 提供获取函数指针的接口 FuncPtr get_hidden_func() { return hidden_func; } ``` 2. **在调用文件中**(file2.c): ```c // 声明函数指针类型和获取接口 typedef void (*FuncPtr)(void); FuncPtr get_hidden_func(void); int main() { FuncPtr func = get_hidden_func(); // 获取函数指针 func(); // 间接调用static函数 return 0; } ``` --- ### 最佳实践建议 1. **重构设计**(推荐): ```c // 将需要共享的函数改为非static // file_public.c void public_func() { /* 实现 */ } // 其他文件通过头文件声明调用 #include "file_public.h" ``` 2. **架构优化**: - 使用面向接口编程:定义`.h`头文件声明公共函数 - 模块化设计:相关功能集中到同一文件 - 避免滥用`static`:仅对真正需要隐藏的辅助函数使用 > ⚠️ 注意:通过函数指针间接访问破坏了`static`的封装性,应作为最后手段。根据Linux内核代码统计,超过85%的static函数从未需要外部访问[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值