Вопрос объявления метода C ++ - PullRequest
4 голосов
/ 06 августа 2009

У меня есть код в Image.cpp:

Image::Image( int width, int height, int depth ) : m_sFileName(0)  
{  
...  
}  

and in Image.h:  
class Image: public DrawAble, public RenderAble  
{  
...  
private :  
    std::string *m_sFileName;  
};  

Мой вопрос: что происходит с m_sFilename в первой строке? Я предполагаю, что это установлено в NULL, но какой смысл делать это таким образом. Было бы то же самое сделать:

Image::Image( int width, int height, int depth )  
{  
    m_sFileName(0);  
...  
}

Ответы [ 7 ]

11 голосов
/ 06 августа 2009

Первый использует так называемый список инициализации .

Когда вы вводите тело конструктора, все члены классов должны быть созданы (чтобы их можно было использовать). Так что, если у вас есть это:

class Foo
{
public:
    Foo()
    : str() // this is implicit
    {
        str = "String.";
    }
private:
    std::string str;
};

Итак, str создается, а затем назначается. Лучше было бы:

class Foo
{
 public:
    Foo()
    : str("String.")
    {
    }
private:
    std::string str;
};

Так что str будет построен напрямую. Это не имеет значения в вашем случае, потому что у указателей нет конструктора.

Обычно считается хорошей практикой использовать список инициализации поверх исполняемого кода в конструкторе. список инициализации должен использоваться для инициализации , конструктор должен использоваться для запуска кода.

Кроме того, зачем использовать указатель на строку? Если вы хотите строку, используйте строку; не указатель на строку. Скорее всего, вы действительно хотите строку.


Подробнее о списках инициализаторов:

Списки инициализаторов имеют больше применений, чем просто инициализация членов класса. Их можно использовать для передачи аргументов в базовые конструкторы:

class Foo
{
public:
    Foo(int i) { /* ... */ }
}

class Bar
    : public Foo
{
public:
    Bar()
    : Foo(2) // pass 2 into Foo's constructor.
             // There is no other way of doing this.
    {
        /* ... */
    }
};

или постоянные члены:

class Foo
{
public:
    Foo()
    : pi(3.1415f)
    {
        pi = 3.1415f; // will not work, pi is const.
    }
private:
    const float pi;
};

Или ссылки:

class Foo
{
public:
    Foo(int& i)
    : intRef(i) // intRef refers to the i passed into this constructor
    {
        intRef = i; // does *not* set intRef to refer to i!
                    // rather, it sets i as the value of
                    // the int intRef refers to.
    }
private:
    int &intRef;
};
2 голосов
/ 06 августа 2009

Это называется инициализатором. Вы должны привыкнуть к их использованию. В этом случае это не имеет значения. Но в других случаях неиспользование их может означать двойную инициализацию не указателя-члена. Сначала со значениями по умолчанию, затем со своими значениями. И, наконец, случай члена без конструктора без параметров. В этих случаях у вас нет выбора, кроме как использовать инициализатор.

0 голосов
/ 06 августа 2009

Эти два варианта почти одинаковы - вы правы, что

: m_sFileName(0)

вызывает инициализацию m_sFileName в 0.

Причина, по которой C ++ имеет этот специальный синтаксис инициализации, становится важной, когда вы хотите создать const Image. (Возможно, это не то, что вы хотите сделать в этом случае, но это может быть то, что вы, возможно, захотите сделать для «облегченных» типов.) Для const Image, this является указателем const в конструкторе, а также в каждом " обычная функция-член, и поэтому m_sFileName=0 не допускается.

Для решения этой проблемы в C ++ есть списки инициализации, которые выполняют инициализацию, а не присваивание. Кстати, если бы m_sFileName был объектом, было бы дополнительное различие помимо соображений const: список инициализации вызовет конструктор m_sFileName, тогда как присваивание вызовет оператор присваивания.

Помимо всех этих соображений, списки инициализации являются хорошим способом сообщить о намерениях - показать, что вы инициализируете, а не назначаете.

0 голосов
/ 06 августа 2009

Синтаксис, который вы используете:

Image::Image( int width, int height, int depth ) : m_sFileName(0)  
{  
...  
}  

называется списком инициализации. Он назначит значение 0 вашей переменной-члену.

Использование m_sFileName = 0; в теле конструктора будет менее производительным, потому что элемент будет инициализирован дважды (один раз автоматически, поскольку он не включен в список инициализации, и второй раз с вашей явной инициализацией).

0 голосов
/ 06 августа 2009

Это так же, как:

Image::Image( int width, int height, int depth )  
{  
    m_sFileName = 0;
 ...  
}
0 голосов
/ 06 августа 2009
m_sFileName(0) 

в теле конструктора будет интерпретироваться как вызов функции с именем m_sFileName. Вы можете заменить его на

m_sFileName = 0;

Однако рекомендуемая инициализация находится в списке инициализации конструктора, как в первом примере. Любой элемент данных, который не инициализирован в списке инициализации конструктора, будет автоматически инициализирован конструктором по умолчанию его типа.

0 голосов
/ 06 августа 2009

Это было бы то же самое, что и

Image::Image( int width, int height, int depth )
{
    m_sFileName = 0;
    // ...
}

Обратите внимание, что использование указателя на std::string, как правило, не очень хорошая идея, так как пустая строка - не менее хороший маркер ничего здесь, и вам не нужно заботиться об уничтожении, если вы делаете его нормальным член.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...