是否可以在相同的名称空间中但在不同的嵌套项目中定义具有相同名称的类?

问题描述 投票:0回答:2

我在Eclipse中有一个大项目,其中包含多个嵌套项目。这些嵌套项目中的每个项目均位于单独的文件夹中,并分别进行编译和链接。最后,它们每个都输出一个单独的静态库。然后,很少有与这些静态库链接的可执行文件。在这些项目中的两个中,我有两个具有相同名称和相同构造函数参数的类,但是它们具有不同的实现和不同的额外成员。这两个类都在同一个名称空间中。编译每个项目后,我将使用它们为单独的可执行文件创建的静态库。每个可执行文件都链接到正确的类,它们不混合实现。一切似乎都正常。

问题是,当我编译一个类时,编译器给我一个错误,我没有初始化一些成员变量,而这些成员变量实际上属于另一个类。在编译过程中,这些类无法相互访问-它们不包含另一个类,或者不包含标头,而标头则包含另一个。它们在单独的项目中,在单独的文件夹中,并且分别进行编译。那么,在编译第一个类时,编译器怎么可能以某种方式寻找第二个类的定义,并给我一个错误,即我尚未从中初始化成员?由于我使用的是Eclipse,而我的项目的结构是这样的:Main C ++ Project(未编译)将其他C ++项目作为嵌套项目(单独编译)保存在其中-可能与Eclipse有关?

我违反了一个定义规则,因为我的类位于单独的项目中,并且它们在单独的编译中进行编译,只有一些通用的头文件,而它们之间没有其他连接?如果是这样,编译器怎么可能捕获到这样的问题,但仍然能够获得正确的类定义?因为可以肯定,它会正确运行,并且一切正常。

所以我的问题是编译器给我的警告,因为我必须在交付代码之前清除所有编译警告。代码本身可以正常工作。

最佳问候

===评论后更新===

首先,很抱歉,不清楚。这是整个项目结构的图像(我显然更改了名称:)):

The structure of my project

我有MainProject项目,该项目充当其他项目的容器。我不建。在nested1中,我有一些项目构成了静态库,然后将它们与Executable1和Executable2链接在一起。在nested2中,我还有其他项目再次构成了静态库,然后将其与Executable3链接。

我在nested1和nested2中都有MyFooBar类。这是他们两个的代码

// nested1/StaticLib5/MyFooBar.hpp
namespace foo
{
    namespace bar
    {
        class MyFooBar : public FooBar
        {
            public:
                   MyFooBar(int a, int b, double c);
                   // Getters and Setters
                   // Other user-defined functions
            private:
                   int memA;
                   int memB;
                   double memC;
                   int memDiff1;
        };
    }
}
// nested2/StaticLib9/MyFooBar.hpp
namespace foo
{
    namespace bar
    {
        class MyFooBar : public FooBar
        {
            public:
                   MyFooBar(int a, int b, double c);
                   // Getters and Setters
                   // Other user-defined functions
            private:
                   int memA;
                   int memB;
                   double memC;
                   int memDiff2;
                   char memDiff3;

        };
    }
}
// nested1/StaticLib5/MyFooBar.cpp
namespace foo
{
    namespace bar
    {
        MyFooBar::MyFooBar(int a, int b, double c) :
        memA{a}, memB{b}, memC{c}, memDiff1{0}
        {
        }         
    }
}
// nested2/StaticLib9/MyFooBar.cpp
namespace foo
{
    namespace bar
    {
        MyFooBar::MyFooBar(int a, int b, double c) :
        memA{a}, memB{b}, memC{c}, memDiff2{0}, memDiff3{0}
        {
        }         
    }
}

Executable1和Executable2不使用nested2中的任何库。 Executable3仅使用静态库StaticLib1 nested1

非可执行文件同时链接到StaticLib5和StaticLib9。另外,在链接可执行文件的过程中没有出现错误,在编译任何MyFooBar类时都会收到警告。警告说:

Member memDiff2 was not initialized in this constructor

这两个类可能唯一的共同点是一个标头,其中有一个类正向声明和一些typedef,这些我在类本身中没有使用。

class MyFooBar;
typedef ListWidget* MyFooBarPtr;
typedef std::vector< MyFooBarPtr > MyFooBarVec;
typedef MyFooBarVec* MyFooBarVecPtr;
c++ class namespaces eclipse-cdt
2个回答
0
投票

让我们考虑编译器在后台执行的操作:我们有两个模块或翻译单元:模块A和B。

在编译期间,我们将得到以下内容:

Module A: namespace::class_name == foo::bar 
Module B: namespace::class_name == foo::bar

在将源代码编译为目标文件的过程中,如您所述,这些文件将分别进行编译。这里的问题与编译器无关。

当它采用所有目标代码或转换单元并尝试将所有内容链接在一起以构建单个可执行文件时,就会出现问题。

这是当您最终遇到确实违反一个定义规则的命名或符号解析冲突时。问题是在链接器内出现的。

然而,在诸如Visual Studio之类的集成开发环境中构建的某些编译器可能具有一些将“向前看”的编译器标志,以便说说看在完成编译之前是否可能发生任何可能的冲突,并且这可以生成编译时警告。甚至是错误,甚至连链接器都没有。

我建议您更改类的名称或名称空间的名称,但是将名称空间的名称保留在类的名称上会更方便,因为那是代表代码库的名称空间。

如果出于某种原因需要将类保留为相同的名称,那么它们可以是另一种选择,即包括一个内部内部嵌套名称空间名称,以解决这种命名冲突,从而减少歧义。

例如:

Module A: namespace::namespace::class_name = foo::ver_1::bar
Module B: namespace::namespace::class_name = foo::ver_2::bar

然后,内部嵌套的名称空间将防止这种命名冲突,只要您不使用以下指令:

#using namespace foo::ver_1;
#using namespace foo::ver_2;

在相同的可见范围内!


0
投票

C ++中有一个定义规则,您不能两次定义类/函数。更不用说定义不同的情况。

就是说,还有一些隔离规则,使您可以绕过一个定义规则。说,您有两个具有相同名称的类,但是它们仅在两个不同的.cpp文件中定义和使用,因此一切正常。

Update:

可能会编译,但会这样做,因为链接器的工作很差。如果定义不匹配,它将无法正常工作。但是,当您具有项目级分隔时(即在不同的共享库文件中也称为.dll),它确实可以工作-需要动态库级分隔,而.cpp或静态库分隔级还不够。感谢@fdan和@FrancisCugler。

[基本上,只要存在以某种方式使用这两个定义的项目-否则就存在强大的编译边界-这违反了一个定义规则并且无法正确编译。

P.S。为什么还要有两个同名的类?这是错误的绝佳来源。如果要在不同体系结构或OS系统之间使用可移植的代码,可以使用#ifdef从构建中删除该类的不合适版本。

© www.soinside.com 2019 - 2024. All rights reserved.