пространства имен, классы и бесплатные функции — когда вам нужны полные имена

В моем примере ниже, почему я должен полностью уточнять имя функции free в cpp, чтобы избежать ошибок компоновщика, и почему это работает для функции класса без? Можете ли вы объяснить разницу?

ctest.h:

namespace Test
{
    int FreeFunction();

    class CTest
    {
        public:
            CTest();
            ~CTest();
    };
}

ctest.cpp:

#include "ctest.h"

using namespace Test;

// int FreeFunction()     -> undefined reference error
int Test::FreeFunction()  -> works just fine
{
    return 0;
}

CTest::CTest()                -> no need to fully qualify name, i.e. Test::CTest
{}

CTest::~CTest()
{}

Спасибо за ваше время и помощь.


person nabulke    schedule 03.11.2010    source источник
comment
Обратите внимание, что указание void в качестве аргумента полезно в C, но бесполезно и считается плохим стилем в C++: parashift.com/c++-faq-lite/newbie.html#faq-29.4   -  person log0    schedule 03.11.2010
comment
@Ugo: я отредактировал свой вопрос, чтобы удалить эту мерзость. Спасибо что подметил это.   -  person nabulke    schedule 03.11.2010


Ответы (4)


int FreeFunction(void);  

это просто объявление, тогда как ниже приведено определение.

class CTest 
{ 
    public: 
        CTest(); 
        ~CTest(); 
}; 

Если вы хотите указать definition for an already declared entity in a namespace (например, во включающем пространстве имен), это должно быть полное имя.

РЕДАКТИРОВАТЬ2:

Вот кое-что, что внесет ясность. Обратите внимание, что в этом коде нет директивы using.

namespace Test { 
    int FreeFunction(void);   // declare

    class CTest;              // declare
} 

int Test::FreeFunction(){return 0;} // define
class Test::CTest{            // define
};

int main(){}

РЕДАКТИРОВАТЬ 3: Декларация против определения (С++ 0x) $ 3.1/2-

Объявление является определением, если только оно не объявляет функцию без указания тела функции (8.4), оно содержит спецификатор extern (7.1.1) или спецификацию связывания25 (7.5), а также ни инициализатор, ни тело-функции, оно объявляет член статических данных в определении класса (9.4), это объявление имени класса (9.1), это непрозрачное-объявление-перечисления (7.2) или объявление typedef (7.1.3), объявление-использования (7.3.3), объявление-static_assert (раздел 7), объявление-атрибута (раздел 7), пустое-объявление (раздел 7) или объявление-использования. директива (7.3.4).

person Chubsdad    schedule 03.11.2010
comment
Правильно ли сказать, что в моем ctest.h я определяю класс CTest, но объявляю только функции-члены CTest? - person nabulke; 03.11.2010
comment
Да ты прав. Кроме того, функция может быть определена встроенной в пространство имен или определена в любом из включающих ее пространств имен с уточнением полного имени. - person Chubsdad; 03.11.2010
comment
Очень хороший ответ с примерами и хорошим объяснением. Помогли разобраться в проблеме. Спасибо - person nabulke; 03.11.2010

Хотя FreeFunction преобразуется в Test::FreeFunction, если вы обращаетесь к нему или вызываете после предоставления строки using namespace Test;, что касается определения функции, компилятор не может узнать, вы определяете совершенно новую функцию FreeFunction вне какого-либо пространства имен или определяете уже объявленную Test::FreeFunction. Компилятор по умолчанию считает, что вы определяете совершенно новую функцию.

Однако для CTest::CTest вы уже имеете в виду класс Test::CTest, а поскольку вне пространства имен Test нет класса или пространства имен CTest, ссылка на CTest::anything недвусмысленна. Таким образом, он знает, что определения конструктора и деструктора относятся к классу в пространстве имен CTest.

Я думаю, что это небольшая цена, чтобы написать Test::FreeFunction.

Надеюсь это поможет!

person btown    schedule 03.11.2010

Если вы не уточняете определение FreeFunction, компилятор не знает наверняка, хотите ли вы предоставить реализацию для ранее объявленного Test::FreeFunction или для отдельной FreeFunction в текущем пространстве имен.

С другой стороны, есть только один способ разрешить имя CTest — как определение класса из пространства имен Test. Таким образом, нет необходимости полностью квалифицировать его.

Однако, если разрешение имени CTest неоднозначно (скажем, в текущем пространстве имен есть еще один класс CTest), вам придется полностью квалифицировать объявления методов.

person Franci Penov    schedule 03.11.2010

Я считаю, что при реализации функции обычно предпочтительнее открывать пространство имен. Помните, что вы можете снова открыть их...

// in Test.cpp
namespace Test
{
   int FreeFunction()
   {
       return 0;
   }
}
person CashCow    schedule 03.11.2010