Форум программистов, компьютерный форум CyberForum.ru

Способ создания экземпляра класса - C++

Восстановить пароль Регистрация
 
Ullaluna
 Аватар для Ullaluna
8 / 6 / 1
Регистрация: 11.11.2013
Сообщений: 75
23.02.2014, 21:02     Способ создания экземпляра класса #1
Ниже в скрипте отметила два варианта. В чужих скриптах встречаю оба время от времени. Интересно, в чем принципиальное различие и как вернее.

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include <iostream>
using namespace std;
 
class A {
};
 
class B : public A {
};
 
int main() {
 A *pa = new B(); //первый вариант записи
 A *pa = new B; //второй вариант записи
 
 return 0;
}
Лучшие ответы (1)
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
23.02.2014, 21:17     Способ создания экземпляра класса #2
В случае классов разницы нет.
Разница заметна для встроенных типов.
Например:
C++
1
2
3
4
5
int * a = new int;
// в *a - мусор
 
int * a = new int();
// в *a - ноль
Добавлено через 4 минуты
Это играет роль, например, когда мы пишем шаблон:
C++
1
2
3
4
5
6
7
8
template <typename T>
T * createObject()
{
// если тут не поставить скобки, то в случае T=='встроенный тип' не будет инициализации нулем
    return new T(); 
}
 
int * p = createObject<int>();
Ullaluna
 Аватар для Ullaluna
8 / 6 / 1
Регистрация: 11.11.2013
Сообщений: 75
23.02.2014, 21:24  [ТС]     Способ создания экземпляра класса #3
Спасибо.

Меня смутило, что вопрос был в тесте и звучал как "создать экземпляр класса, используя new". Два другие варианта были точно неверными, остаются два, которые я привела.
Неграмотность составителя теста я, в принципе, не исключаю. Но может, есть разница в плане "хорошего стиля", знания каких-то тонкостей и подсознательных комплексов оператора new?..
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
23.02.2014, 21:42     Способ создания экземпляра класса #4
Цитата Сообщение от Ullaluna Посмотреть сообщение
Но может, есть разница в плане "хорошего стиля", знания каких-то тонкостей и подсознательных комплексов оператора new?..
В случае класса (типа, объявленного ключевым словом class) - оба варианта полностью эквивалентны.
Если у нас не класс, а структура, то будут различия в том случае, если структура - POD.

А писать или не писать скобки может зависеть от многих вещей, например выделение памяти и отложенная инициализация. Очевидно в этом случае инициализация по умолчанию нулями не нужна.
С точки зрения обобщения, конечно, вариант со скобками предпочтительнее (см. пример с шаблоном), но однозначных рекомендаций "хорошо" или "плохо", лично я бы не стал давать.
Tulosba
:)
Эксперт С++
4378 / 3221 / 297
Регистрация: 19.02.2013
Сообщений: 9,044
23.02.2014, 22:10     Способ создания экземпляра класса #5
DrOffset, не важно класс или структура. Важно наличие или отсутствие конструктора по умолчанию. Если конструктора нет, то при записи со скобками члены будут инициализированы значением по умолчанию, при записи без скобок члены (фундаментальные типы) останутся не проинициализированы. Если конструктор есть, то он будет вызван в любом случае и члены будут инициализированы в соответствием с кодом в конструкторе.
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
23.02.2014, 22:37     Способ создания экземпляра класса #6
Цитата Сообщение от Tulosba Посмотреть сообщение
не важно класс или структура.
Согласен, не важно.
Остальное все собственно вытекало из определения POD. Определяя конструктор, мы уходим от POD. Делая, членом класса std::string - мы так же уходим от POD, однако конструктор можно и не определять. Следовательно первично наличие POD, а конструктор уже как следствие.
Tulosba
:)
Эксперт С++
4378 / 3221 / 297
Регистрация: 19.02.2013
Сообщений: 9,044
23.02.2014, 22:57     Способ создания экземпляра класса #7
Цитата Сообщение от DrOffset Посмотреть сообщение
Определяя конструктор, мы уходим от POD.
Согласен. Можно также и функцию виртуальную добавить.
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
23.02.2014, 22:58     Способ создания экземпляра класса #8
Я размышлял откуда взялось мое заблуждение насчет класса и понял, что ноги растут из стандарта С++03:
An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1), no private or protected non-static data members (clause 11), no base classes (clause 10), and no virtual functions (10.3).
Так как в классе по-умолчанию действует private. Однако стоило определить класс с public POD полями - он тоже становился POD.
В С++11 сильно ослабили эту формулировку и, насколько мне известно, теперь такого ограничения нет (как и некоторых других).
В любом случае твое замечание абсолютно правомерно, спасибо
Tulosba
:)
Эксперт С++
4378 / 3221 / 297
Регистрация: 19.02.2013
Сообщений: 9,044
23.02.2014, 23:05     Способ создания экземпляра класса #9
Цитата Сообщение от DrOffset Посмотреть сообщение
В С++11 сильно ослабили эту формулировку
В C++11 POD = тривиальный + стандартное размещение.
А на тему struct/class уже как аксиома - полностью заменимы за исключением уровня доступа по умолчанию.
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
23.02.2014, 23:08     Способ создания экземпляра класса #10
Цитата Сообщение от Tulosba Посмотреть сообщение
В C++11 POD = тривиальный + стандартное размещение.
Именно.
Я говорил о том, что в С++03 вот такой класс:
C++
1
2
3
4
class A
{
    int a;
};
не являлся POD. Однако в С++11 - он POD является.
Tulosba
:)
Эксперт С++
4378 / 3221 / 297
Регистрация: 19.02.2013
Сообщений: 9,044
23.02.2014, 23:18     Способ создания экземпляра класса #11
DrOffset, правильно ли я понял, что тогда в C++03 такой код:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <iostream>
 
class A
{
    int a;
    friend void f( A* a );
};
 
void f( A* a ) 
{
    std::cout << a->a << std::endl; 
}
 
int main() {
 
    A* a = new A(); 
    f( a );
 
    return 0;
}
должен выдать мусор несмотря на наличие скобок?
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
23.02.2014, 23:29     Способ создания экземпляра класса #12
Цитата Сообщение от Tulosba Посмотреть сообщение
должен выдать мусор несмотря на наличие скобок?
Да, но я ни разу не наблюдал такого на практике. Однако вряд ли все компиляторы, на которых я пробовал, настолько строго соответствовали стандарту. Более того, даже определяя виртуальную функцию, переменная все равно в этом случае будет ноль (в GCC).
Tulosba
:)
Эксперт С++
4378 / 3221 / 297
Регистрация: 19.02.2013
Сообщений: 9,044
23.02.2014, 23:41     Способ создания экземпляра класса #13
Цитата Сообщение от DrOffset Посмотреть сообщение
Да, но я ни разу не наблюдал такого на практике.
Проверил на VS 2010/2012, получаю 0. При этом добавление виртуальной функции - сразу мусор (0xCDCDCDCD), т.е. не инициализированная память. Так что с C++03 не очень ясно.
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
23.02.2014, 23:44     Способ создания экземпляра класса #14
Цитата Сообщение от Tulosba Посмотреть сообщение
Так что с C++03 не очень ясно.
Я привел цитату из стандарта
Тут все ясно, в том числе и то, что Visual Studio, как и GCC, в этом плане ему не соответствуют.
kvadro
11 / 9 / 1
Регистрация: 12.03.2012
Сообщений: 127
24.02.2014, 02:28     Способ создания экземпляра класса #15
должен выдать мусор несмотря на наличие скобок?
Тут вообще интересное поведение с этими значениями по умолчанию.

Непонятное поведение
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
24.02.2014, 20:14     Способ создания экземпляра класса
Еще ссылки по теме:

Явное создание экземпляра класса и явная специализация шаблона класса C++
C++ Создание нового экземпляра дочернего класса из экземпляра базового
Передача свойства одного экземпляра класса другому экземпляру класса C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
DrOffset
6461 / 3835 / 886
Регистрация: 30.01.2014
Сообщений: 6,630
24.02.2014, 20:14     Способ создания экземпляра класса #16
Сообщение было отмечено автором темы, экспертом или модератором как ответ
В общем ситуация оказалась несколько сложнее.
Во-первых я был не прав в своей оценке поведения согласно стандарту С++03.
Итак, для начала разберемся что такое POD:
9/4
A POD-struct is an aggregate class that has no non-static data members of type non-POD-struct,
non-POD-union (or array of such types) or reference, and has no user-defined copy assignment operator
and no user-defined destructor.
Смотрим что такое aggregate class:
8.5.1/1
An aggregate is an array or a class (clause 9) with no user-declared constructors (12.1), no private or pro-
tected non-static data members
(clause 11), no base classes (clause 10), and no virtual functions (10.3).
Эти пункты эквивалентны для стандартов С++03 и С++98. Из них следует, что класс объявленный так:
C++
1
2
3
4
class A
{
    int a;
};
НЕ является POD-классом. И это действительно так, согласно вышеупомянутых стандартов (С++11 пока не касаемся).
Tulosba предположил, что в примере:
Кликните здесь для просмотра всего текста
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class A
{
    int a;
    friend void f( A* a );
};
 
void f( A* a ) 
{
    std::cout << a->a << std::endl; 
}
 
int main() {
 
    A* a = new A(); 
    f( a );
}

Не будет вследствие этого выполняться инициализация поля a и я согласился с этим. Как оказалось - это не так.
В стандарте С++03 есть три типа инициализации: zero-initialization, default-initialization и value-initialization.
В рамках стандарта С++03 вышеуказанный пример приводит как раз к value-initialization. Т.к. тип не является POD и отсутствует user-defined конструктор копирования.
8.5/5
To value-initialize an object of type T means:
— if T is a class type (clause 9) with a user-declared constructor (12.1), then the default constructor for T is
called (and the initialization is ill-formed if T has no accessible default constructor);
if T is a non-union class type without a user-declared constructor, then every non-static data member
and base-class component of T is value-initialized;

— if T is an array type, then each element is value-initialized;
— otherwise, the object is zero-initialized
Именно второй пункт подходит под наш случай. И т.к.:
8.5/7
An object whose initializer is an empty set of parentheses, i.e., (), shall be value-initialized.
мы видим, что инициализация нулем в этом случае абсолютно правомерна. Однако, если добавить виртуальную функцию в VS2012, то инициализация нулем перестанет работать. Это и есть баг компилятора по отношению, к стандарту С++03 - т.к. с точки зрения вышеуказанных пунктов ничего не изменилось. Почему так происходит? Потому что студия продолжает в этом плане действовать в рамках стандарта С++98, в котором было только два типа инициализации: zero-initialization и default-initialization.
Параграф 8.5/5 имел другой вид (отсутствовала value-initialization, вместо нее в этом случае использовалась default-initialization):
To default-initialize an object of type T means:
if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is
ill-formed if T has no accessible default constructor);

— if T is an array type, each element is default-initialized;
— otherwise, the storage for the object is zero-initialized.
и условие, которое его активировало тоже 8.5/7:
An object whose initializer is an empty set of parentheses, i.e., (), shall be default-initialized.
Следовательно можно сделать вывод, что мусор в переменной a, если рассматривать стандарт С++98, должен быть в обоих случаях (с виртуальной функцией и без). Если рассматривать стандарт С++03, то мусор при наличии () должен появиться только если определен конструктор не проводящий инициализацию поля а. В GCC, к слову сказать, инициализация нулем проходит (наличие вирт. функции, приватных полей, все, кроме явно опр. конструктора), даже в режиме С++98, что тоже неверно.

Исходя и вышесказанного можно рассмотреть такой пример:
C++
1
2
3
4
5
6
// POD
struct A { int a; }; 
// не-POD, конструктор по-умолчанию сгенерирован компилятором
struct B { virtual void foo() {}; int a; }; 
// не-POD, инициализация по-умолчанию переменной а
struct C { C() : a() {}; int a; };
В С++98 это приводит:
C++
1
2
3
4
5
6
7
8
new A;   // нет инициализации, в a - мусор
new A(); // zero-initialization, в a - ноль
 
new B;   // default-initialization, a - мусор
new B(); // default-initialization, а - мусор
 
new C;   // default-initialization, а - ноль
new C(); // default-initialization, а - ноль
В С++03 немного иначе:
C++
1
2
3
4
5
6
7
8
new A;   // нет инициализации, в а - мусор
new A(); // value-initialization, которая приводит к zero-initialization, поскольку это POD
 
new B;   // default-initialization, в а - мусор
new B(); // value-initialization, которая приводит к zero-initialization в соответствии с 8.5/5, в а - ноль
 
new C;   // default-initialization, вызов конструктора, в а - ноль
new C(); // value-initialization, вызов конструктора, в а - ноль
Пункт про default-initialization в С++03 соответствует С++98.

В С++11 изменилась формулировка и класс с приватными полями так же может считаться POD-классом.
Кликните здесь для просмотра всего текста
9/10
A POD struct is a class that is both a trivial class and a standard-layout class, and has no non-static
data members of type non-POD struct, non-POD union (or array of such types).
9/7
A standard-layout class is a class that:
— has no non-static data members of type non-standard-layout class (or array of such types) or reference,
— has no virtual functions (10.3) and no virtual base classes (10.1),
— has the same access control (Clause 11) for all non-static data members,
— has no non-standard-layout base classes,
— either has no non-static data members in the most derived class and at most one base class with
non-static data members, or has no base classes with non-static data members, and
— has no base classes of the same type as the first non-static data member.
Yandex
Объявления
24.02.2014, 20:14     Способ создания экземпляра класса
Ответ Создать тему
Опции темы

Текущее время: 20:55. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru