【问题标题】:Is it possible to define classes with same name in same namespace but in different nested projects?是否可以在同一个命名空间但在不同的嵌套项目中定义具有相同名称的类?
【发布时间】:2020-02-05 09:00:49
【问题描述】:

我在 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;

【问题讨论】:

  • 文字过多。没有代码。这是一个糟糕的问题。
  • 同一个命名空间中的两个同名类在链接发生时会发生冲突,是的。
  • 为什么现在这样有效,取决于您的项目特有的许多变量。有一天它可以停止工作。所以你最好把它修好。
  • @RSahu:我添加了示例代码。
  • @Chad:我不会同时链接他们两个。

标签: c++ class namespaces eclipse-cdt


【解决方案1】:

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

也就是说,还有一些隔离规则允许您绕过一个定义规则。比如说,你有两个同名的类,但它们只在两个不同的 .cpp 文件中定义和使用,那么一切都应该工作......

更新:它可能会编译,但它会这样做,因为链接器做得不好。如果定义不匹配,它将无法正常工作。但是当您进行项目级别分离时,它确实有效,即在不同的共享库文件(即 .dll)中 - 需要动态库级别分离,.cpp 或静态库分离级别是不够的。感谢@fdan 和@FrancisCugler。

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

附:为什么甚至有两个具有相同名称的类?这是一个极好的错误来源。如果您想在不同的架构或操作系统系统之间拥有可移植的代码,您可以使用 #ifdef 从构建中删除不合适的类版本。

【讨论】:

  • “比如说,你有两个同名的类,但它们只在两个不同的 .cpp 文件中定义和使用,那么一切都应该正常工作。” - 我强烈反对。
  • 这很重要,因为当编译器尝试查找该类的构造函数的声明和定义的名称时,它会选择两者之一,但不能保证它会选择正确的一个。仍然存在命名冲突。
【解决方案2】:

让我们考虑一下编译器在幕后做了什么:我们有两个模块或翻译单元:模块 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;

在同一可见范围内!

【讨论】:

    【解决方案3】:

    首先,感谢您的帮助。问题似乎出在 QNX Momentics IDE 本身。如果我从命令行构建项目,则不会打印任何警告。如果我从 nested1 构建第一个 MyFooBar,然后从 nested2 构建一个项目,然后从 nested2 构建第二个 MyFooBar,我会收到此警告。如果我构建第一个 MyFooBar,然后从 nested1 构建其他几个项目,然后是第二个 MyFooBar,则没有此类警告。在这两种情况下,构建都很好,一切都按预期工作。

    最好的问候, 艾哈迈德

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-09-15
      • 2010-11-12
      • 2013-04-17
      • 2014-01-12
      • 1970-01-01
      • 2019-12-20
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多