对C++类的用法感到困惑

Confused about C++ class usage

本文关键字:用法 C++      更新时间:2023-10-16

下面的代码让我对类的用法有点困惑。

    template <int dim>
    class FEM
    {
    public:
      FEM (unsigned int order,unsigned int problem); 
      ~FEM(); 
    ...
    FESystem<dim>        fe;
    DoFHandler<dim>      dof_handler;
    ...
    }
    template <int dim>
    FEM<dim>::FEM(unsigned int order,unsigned int problem)
    :
    fe (FE_Q<dim>(order), dim), 
    dof_handler (triangulation)
    {
    ...
    }

这里的"FESystem"、"DoFHandler"answers"FE_Q"是头文件中的一些预定义类。我对这个代码有几个问题:

(1) 为什么构造函数"fe"answers"dof_handler"在类"fe"本身之外声明,它们是否可能在第一个括号内声明,即在"fe"类定义内?

(2) 代码中的双冒号和冒号""answers""分别有什么含义?为什么在这里使用它们?冒号":"是指继承吗?

    template <int dim>
    FEM<dim>::FEM(unsigned int order,unsigned int problem)
    :
    fe (FE_Q<dim>(order), dim), 
    dof_handler (triangulation){...}

我是C++新手。有人能帮我吗?非常感谢!

(1) 为什么构造函数"fe"answers"dof_handler"在类"fe"本身之外声明,它们是否可能在第一个括号内声明,即在"fe"类定义内?

是的,对于模板化类,它们可以在"FE"模板类定义的内部或外部。有些人更喜欢将它们放在外部以便于阅读,在那里他们可以看到所有可用函数的列表,而不必向下滚动太远。

但是,对于常规的非模板类,请将类外实现放在相应的源(.cpp)文件中,而不是放在类下面或类中。原因是,包含您的头的其他所有类都将一遍又一遍地编译相同的实现,这将减慢您的构建速度,并生成巨大的二进制文件。对于模板化类,您别无选择。它们必须由每个翻译单元编译,因为模板化的代码将由编译器生成。

(2) 代码中的双冒号和冒号"::"answers":"分别有什么含义?为什么在这里使用它们?冒号":"是指继承吗?

双冒号是你的范围。它可以是名称空间或类名。这使得两个不同类中的两个函数名称完全相同而不会发生名称冲突成为可能。它对于覆盖非常有用。

例如,您可以在标题中包含以下内容:

class A
{
public:
    virtual foo(int a);
};
class B : public A
{
public:
    virtual foo(int a);
};

并将其保存在您的源文件(.cpp)中:

A::foo(int a) {
    printf("Hello World from A::foo(%s)n", a);
}
B::foo(int a) {
    A::foo(a); // calling the super class's foo(int)
    printf("Hello World from B::foo(%s)n", a);
}

如果我们没有范围,我们就无法区分这两个函数。在Java或C#中,它被简化为一个单点。

在这种特殊情况下,单个冒号是C++构造函数的一个非常特殊的特性。只有构造函数才能执行此操作。这被称为"初始值设定项",它们是一个巧妙的小优化,可以用来为成员变量设置默认值,比在构造的函数范围内手动设置更快。

例如,如果您在这里有这样的类:

class A
{
public:
    A(); // constructor
    int a;
    float b;
};

您可能会想在.cpp源文件中这样编写构造函数:

A::A() {
    a = 0;
    b = 0.0;
}

但相反,通过初始化,您可以这样写:

A::A() :
    a(0),
    b(0.0)
{
}

只要你按照与它们在头中声明的顺序相同的顺序初始化它们,它们就可以更快地初始化你的内存。在这个特定的示例中,如果在a之前初始化b,则优化将不起作用。

C++一开始可能很难理解,但它是一种非常强大的语言,学习起来非常有趣。

双冒号用于指定您正在为特定类实现函数。在这种情况下,它是FEM类构造函数的实现。如果FEM有一个方法DoIt(),那么实现可能看起来像:

template<int dim>
int FEM<dim>::DoIt() { return 1; }

如果要将参数直接传递到成员变量的构造函数中,则可以将单个冒号":"与构造函数一起使用。因此,在上面的代码中,FEM的构造函数将参数传递给它的成员fe。FESystem必须具有一个构造函数,该构造函数接受FE_Q<lt;dim>>和一个int作为参数。它还将一些内容传递给DoFHandler,但不清楚代码中的triangulation是什么。这看起来不像是要编译的。

C++中的类方法通常在类定义之外定义。这与Java、Python等不同。有一个很好的理由:类的定义只包含其方法的原型,即所谓的声明。这是程序的其他部分了解特定类的外观所需的所有信息。类定义保存在头文件中。这些头需要包含在使用该类的每个源代码中。

方法的实际实现通常保存在一个单独的文件中。因此,类名和它前面的双冒号用于标识它们所属的类。

头文件中的源代码将与使用这些类定义的每一段代码一起编译。这些方法背后的实际程序可以单独编译,并在链接阶段添加。