从构造函数调用成员函数

19

我知道这个问题的标题与C++:在构造函数中调用成员函数?相似,但我正在问一个更通用的问题。

在构造函数中调用成员函数是一种好的实践吗?它使得代码更易读,并且我更喜欢采用封装的方式(即每个代码块都有一个单一的目标)。

以下是一个Python的例子:

class TestClass:
    def __init__(self):
        self.validate()

    def validate(self):
        # this validates some data stored in the class

相较于在构造函数内编写验证代码,这是更好的方法吗?这种方法有什么缺点吗?例如,它是否会增加函数调用的开销?

个人而言,我更喜欢这种方法,因为它更易读,但这只是我的个人偏好。

干杯

6个回答

16

我认为在构造函数中调用成员函数并没有本质上的问题,前提是这些成员函数不是虚函数

从构造函数中调用虚成员函数的问题在于子类可以重写该函数。这将导致构造函数调用子类中被重写的实现,而在调用子类部分对象的构造函数之前。

在Java中,任何一个私有的(private)静态的(static)最终的(final)访问修饰符都可以使方法安全地从构造函数中调用,通过防止对超类方法的虚拟调用。我认为这些技术在Python中不可用。


实际上,在构造函数中调用虚方法非常有用。例如,可以使用虚方法获取特定类类型以在基础构造函数中实例化,其中特定类是已知基类的后代或已知接口的实现者。这样,您可以将实例化和销毁保留在基类中,同时允许后代进行自定义组合。 - Marjan Venema
你是否推荐在各种编程语言中将“在未初始化的对象上调用方法”作为通用实践?我更倾向于使用AbstractFactory和/或Builder设计模式来解决你的示例问题。 - richj

4

您需要注意至少有一个相关的“注意事项”:

N3797 12.6.2/14


(注:此处为原文,无需翻译)

Member functions (including virtual member functions, 10.3) can be called for an object under construction. Similarly, an object under construction can be the operand of the typeid operator (5.2.8) or of a dynamic_cast (5.2.7). However, if these operations are performed in a ctor-initializer (or in a function called directly or indirectly from a ctor-initializer) before all the mem-initializers for base classes have completed, the result of the operation is undefined. [Example:

class A {
public:
   A(int);
};

class B : public A {
    int j;
public:
    int f();
    B() : A(f()),  // undefined: calls member function
                   // but base A not yet initialized
    j(f()) { }     // well-defined: bases are all initialized
};

class C {
public:
    C(int);
};

class D : public B, C {
    int i;
public:
    D() : C(f()), // undefined: calls member function
                  // but base C not yet initialized
    i(f()) { }    // well-defined: bases are all initialized
};

— end example]


3
这种做法的主要问题在于成员函数必须使用一个可能只被部分初始化的对象。如果它(即使是意外地)向其他位置传递了对该对象的引用,那么其他代码就必须执行相同的操作。这可能会变得非常混乱和容易出错,特别是一旦您开始在子类中覆盖这样的函数。

因此,通常应避免或至少限制使用这种做法的函数不能被覆盖,并且它们不应将对正在构造的对象的引用传递给任何其他代码。

1

我比较熟悉C++而不是Python,但我认为从构造函数中调用成员函数没有问题,特别是当这种做法能够从多个构造函数中提取相似的代码时。任何减少冗余的东西都是好的。


0
首先,这个成员函数不能是虚函数, 其次,这个成员函数必须在同一个文件中实现,如果你在*.h中声明了它,那么在*.cpp中实现它,gcc/clang会报告这个错误undefined reference to

这并没有提供比10年前在这里提供的答案更有价值的观点。 - Human-Compiler

0

从可读性的角度来看,这绝对是更好的。不过你需要问自己一个问题,那就是在对象初始化之后是否允许运行验证方法。如果不允许,你可以选择 a) 使用某种私有的 initialized 变量或 b) 使用建造者模式 在使用对象之前将其设置为有效状态。

确保该函数是私有的。你不希望子类覆盖它(除非这是设计所需的,在这种情况下,请将其设为抽象/虚拟函数)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接