Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1] 2  Все   Вниз
  Печать  
Автор Тема: Странная проблема при вызове конструкторов  (Прочитано 22342 раз)
0 Пользователей и 1 Гость смотрят эту тему.
haker-kirik
Гость
« : 20-04-2008 19:56 » 

Вот простой пример:
Код:
#include <string.h>
#include <iostream>
using namespace std;

class base
{
int base_i;
public:
base(){cout << "Default constructor\n";}
base(char s[ ]){cout << "Copy constructor.\n";}
};

class der:public base
{
public:
der(char s[ ])
{
if (strlen(s)==0)
base("N/A");
else
base(s);
}
};

int main()
{
int (a);
der d1("");
der d2("sample str");
cin.get();
return 0;
}
По идее эта программа должна вывести два раза фразу "Copy constructor.", но на самом деле выводится следующее:
Код:
Default constructor
Copy constructor.
Default constructor
Default constructor
Как это понимать???
Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #1 : 20-04-2008 20:50 » 

Всё логично. Два Default Constructor (1я строка и 3я) - результат собственно конструирования объектов класса der.
Вторая и четвёртая строки - результат локального создания двух безымянных объектов класса base в конструкторе класса der. Если сделаешь классу base ещё и деструктор с выводом сообщения, то легко в этом убедишься.
Отдельный вопрос, почему при передаче строки во втором случае вызывается конструктор класса base без параметров. Если создавать именованный объект, то вызывается конструктор base(char s[]). Если неименованный, то конструктор по умолчанию, если не выполнить явное приведение типа base ((char*)str);.
Записан
Антон (LogRus)
Глобальный модератор

ru
Offline Offline
Пол: Мужской
Внимание! Люблю сахар в кубиках!


WWW
« Ответ #2 : 21-04-2008 04:13 » 

согласен с Вадом.
строку base(s) заменил на base d1(s);
стал вызываться копирующий конструктор. очень странно. должен был и так вызваться.
в твоём коде есть ряд ошибок поправил код думаю по коду поймёшь где твои ошибки
Код:
class base
{
int base_i;
public:
base()
{
std::cout << "Default constructor\n";
}
base(const char * s)
{
std::cout << "Copy constructor.\n";
}
};

class der:public base
{
public:
der(const char * s):base(s)
{
base b1(s);
if (strlen(s)==0)
base b2("N/A");
else
base b3(s);
}
};

int _tmain(int argc, _TCHAR* argv[])
{

int (a);
der d1("");
der d2("sample str");
return 0;
}
компилил в VS2003 и в gcc
Записан

Странно всё это....
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #3 : 21-04-2008 05:07 » 

У меня осталось подозрение, что эти конструкции:
Код:
if (strlen(s)==0)
base ("N/A");
else
base (s);
затевались ради вызова конструктора предка для объекта класса der из его же собственного конструктора. Если это так, то правильно будет делать, как у LogRus,
Код:
der(const char * s):base(s)
{
...
}
Записан
haker-kirik
Гость
« Ответ #4 : 21-04-2008 14:11 » 

Вад, именно так. Я хочу вызвать конструктор класса base, а не создавать отдельный объект. Т.е. я хочу увеличить функциональность класса base за счёт создания производного класса der.
В иной реализации данного примера можно и вовсе просто проверять в конструкторе der какое либо условие входящего аргумента и если оно (условие) не выполняется, то обект не создавать, иначе - вызывать конструктор класса base для инициализации нужных значений.
« Последнее редактирование: 21-04-2008 14:26 от Вад » Записан
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #5 : 21-04-2008 14:24 » 

В принципе, из конструктора конструктор предка можно вызвать и из тела, причём именно применительно к данному объекту.
Код:
if (strlen(s)==0)
    base::base("N/A");
else
    base::base(s);
Если не указываешь область видимости, компилятор думает, что это конструирование безымянного объекта. А так - вызывает метод для текущего объекта.
Но по-мне, так некрасиво рисовать Улыбаюсь Тогда уж вынести инициализацию в виртуальный метод Улыбаюсь А отменить конструирование не получится - или делай фабричный метод, или генери исключение из конструктора Улыбаюсь
« Последнее редактирование: 21-04-2008 14:29 от Вад » Записан
haker-kirik
Гость
« Ответ #6 : 21-04-2008 21:09 » 

Вад, большое спасибо! Теперь-то всё заработало.
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #7 : 16-05-2008 06:03 » 

Что заработало? Вообще ничего не понял... Собственно разбираться начал только из-за этого
Цитата
" А так - вызывает метод для текущего объекта."
Никогда так не делал, понятно, что так не пишут, но сожрало любопытство, так вот вот исходник

Код:
#include <iostream>

using namespace std;

class base
{
int base_i;
public:
base()
{
std::cout << "Default constructor\n";
}
base(const char * s)
{
std::cout << "Copy constructor.\n";
}
~base()
{std::cout << "destructor.\n";}
};

class der:public base
{
public:
der(const char * s):base(s)
{
if (strlen(s)==0)
base::base("N/A");
else
base::base(s);
}
};

int main(int argc, char * argv[])
{
// int (a);
der d1("");
der d2("sample str");
return 0;
}

Результат выполнения
Copy constructor.
Copy constructor.
destructor.
Copy constructor.
Copy constructor.
destructor.
destructor.
destructor.

Так вот при этом
base::base("N/A");
и этом
base::base(s);
происходит создание двух временных объектов

Кстати к создателю кода - вопрос, зачем вот это
Код:
base("N/A");
твоя программа никогда не напишет в консоли "N/A"

Итог все что-то поняли и порешали, я вот например ничего не понял... а смысл исходного кода так вообще для меня загадка... если уже внутри конструктора объекта вы решили его не создавать вам может помочь только исключение и более ничего. Итак повторю - я вообще ничего не понял ни одного поста (за исключением второго с которым полностью согласен).

Прям моему удивлению нет предела... где должен вызываться конструктор по умолчанию (ну нет в программе места где он вызывается)? Это
Код:
int (a);
просто чума...
Записан

С уважением Lapulya
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 16-05-2008 06:57 » 

lapulya, хм. Я не знаю, по какому принципу я трассировал подобный твоему код в прошлый раз, но почему-то посчитал, что метод вызывается для объекта. Судя по всему, впал в заблуждение - по крайней мере, сейчас мне не удаётся получить тот же результат, всё говорит о том, что происходит конструирование нового объекта. Пока будем считать, что это моя ошибка Улыбаюсь
Записан
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #9 : 16-05-2008 08:07 » 

Если жуть как надо воспользоваться конструктором как функцией, чисто для спортивного интереса (ну кто ж та делает в жизни...), то надо так переписать конструктор так
Код:
der(const char * s):base(s)
{
if (strlen(s)==0)
this->base::base("N/A");
else
this->base::base(s);
}

Еще раз говорю, это никому не надо, но для эксперимента покатит
Записан

С уважением Lapulya
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 16-05-2008 08:17 » 

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

Тем не менее, если уж вызов this->base::base() возможен, то не понимаю, почему base::base не даёт эффекта. Указание области видимости, вроде, ясно даёт компилятору понять, что речь идёт о методе, а не об объявлении безымянного объекта.
Записан
sss
Специалист

ru
Offline Offline

« Ответ #11 : 16-05-2008 08:56 » 

Оба на сколько эмоций Улыбаюсь ... Конечно, если до конца выверять, то в примерах нет ни одного копирующего конструктора. А в первом топике (вывод в консоль), как могла получиться четвертая строка Default constructor Не понял
Записан

while (8==8)
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #12 : 16-05-2008 10:13 » 

to sss
Цитата
...Конечно, если до конца выверять, то в примерах нет ни одного копирующего конструктора. А в первом топике (вывод в консоль), как могла получиться четвертая строка Default constructor ...
Дык, я о чем и говорю! Даже больше, там ни первой, ни третьей строчки быть не может (что уж говорить о четвертой Ага)!

to Вад
Не понятно почему ты считаешь, что при указании области видимости конструктор должен отрабатывать как функция ведь конструкция
Код:
base::base()
не чуть не лучше и не хуже
Код:
this->base::base()
с точни зрения написания (я про громоздкость), но во втором случае я гарантированно вызываю метод ,хотя подобные рассуждения это чистой воды философия... Все подобные вопросы решает стандарт, там это должно быть описано {если нас слышит Серега, то это я ему  Отлично }
« Последнее редактирование: 16-05-2008 10:15 от lapulya » Записан

С уважением Lapulya
McZim
Команда клуба

ru
Offline Offline
Пол: Мужской
Я странный


WWW
« Ответ #13 : 16-05-2008 10:36 » 

Код:
base::base()
не чуть не лучше и не хуже
Код:
this->base::base()

меньше прерываний процессора Улыбаюсь
Записан

The CBO without stats is like a morning without coffee. (c) T.Kyte.
Вад
Модератор

ru
Offline Offline
Пол: Мужской

« Ответ #14 : 16-05-2008 14:28 » 

Не понятно почему ты считаешь, что при указании области видимости конструктор должен отрабатывать как функция
Мне, напротив, непонятно, почему это не так. Ведь если просто base ещё можно по умолчанию интерпретировать как тип, то base::base() как тип интерпретироваться не должно. Или? Понятно, что это философия, и стандарт всех рассудит
Цитата: ANSI ISO/IEC 14882
12.1
2. A constructor is used to initialize objects of its class type. Because constructors do not have names, they are never found during name lookup; however an explicit type conversion using the functional notation (5.2.3) will cause a constructor to be called to initialize an object. [Note: for initialization of objects of class type see 12.6. ]
Только не понимаю, чем это мотивировано.
Записан
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #15 : 17-05-2008 06:19 » 

Код:
this->base::base()

а это компилируется что ли ? Улыбаюсь

и при чём тут прерывания процессора, чёта я не совсем понял
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #16 : 17-05-2008 10:55 » 

Ну а чего бы ему не компилироваться то? Почему вообще появились в этом сомнения?

Про прерывания процессора я тоже не понял, но воизбежании флуда для себя решил что это шутка (типа для написания последней фразы надо больше писать и типа на обработку этого потребуется больше тактов процессора ))) )
Записан

С уважением Lapulya
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #17 : 17-05-2008 14:45 » 

lapulya, this->base::base()  - ну не знаю , так то компилится, я попробовал , но как-то непривычно и криворуко запись выглядит Улыбаюсь
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #18 : 17-05-2008 20:31 » 

 Отлично Отлично Отлично ну а вызов просто функции мембера тебя не напрягает? Если он например записан так
this->Base::function() Ага
Записан

С уважением Lapulya
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #19 : 17-05-2008 21:07 » 

lapulya, а я и так не делаю Улыбаюсь Мне проще написать

function();

или

this->function(); //хотя тоже лишнее

а это
this->Base::function()
 - это для статик-мембера
Записан

lapulya
Молодой специалист

ru
Offline Offline

« Ответ #20 : 17-05-2008 21:14 » 

Не, для статик мембера это вообще не скомпилируется (тут же у тебя this написано, а для статика никакого this по определению быть не может), для статика так

Base::function();

да и то если вызов идет из внешнего места для скласса, мембером которого является данный статик метод, а если мы в границах родного класса, тогла и так

function();

прокатит
Записан

С уважением Lapulya
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #21 : 17-05-2008 21:23 » 

lapulya, я не проверял про this->Base::function() для статика. А ты проверял ?

просто в любом случае я так никогда не вызываю
this->....

поэтому и не парюсь никогда такими надуманными проблемами )
Записан

Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #22 : 17-05-2008 21:29 » 

Алексей1153++, Это иногда полезно. Например такой случай
Код:
void SomeClass::setColor(int color)
{
   this->color = color;
}
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #23 : 17-05-2008 21:31 » 

Finch, ду ну, зачем себя запутывать ) Это решается проще

void SomeClass::setColor(int color_in)
{
   m_color = color_in;
}
Записан

Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #24 : 17-05-2008 21:45 » 

Алексей1153++, Я не люблю давать сто имен для одной сушности. Иногда приходится и так писать. Хотя в последнее время внутрении переменные класса я стал обозначать через приставку _ , то есть _color.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
lapulya
Молодой специалист

ru
Offline Offline

« Ответ #25 : 17-05-2008 21:53 » 

Опааа to Алексей1153++, я был не прав, беру свои слова обратно для статик функции такой вызов
this->Base::function()
корректен, по крайней мере со стороны MS VS 2005 С++ компилятора.

Для статистики ))) все мемберы переменные у меня имеют приставку the типа
class A
{
int theNumber;
};
Записан

С уважением Lapulya
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #26 : 18-05-2008 08:18 » 

Алексей1153++, Я не люблю давать сто имен для одной сушности. Иногда приходится и так писать.
ну, всё зависит от привычки и внимательности Улыбаюсь Я вот часто бываю невнимателен, поэтому использую префиксы для автокомментирования переменных, что ооочень мне помогает Улыбаюсь



все мемберы переменные у меня имеют приставку the типа
class A
{
int theNumber;
};

а я ставлю "m_" )
Записан

LifeMaker
Гость
« Ответ #27 : 31-05-2008 18:49 » 

Вот простой пример:
...
По идее эта программа должна вывести два раза фразу "Copy constructor.", но на самом деле выводится следующее:
Код:
Default constructor
Copy constructor.
Default constructor
Default constructor
Как это понимать???

На самом деле твою задачу надо было решать так:
Код:
#include <string.h>
#include <iostream>
using namespace std;

class base
{
int base_i;
public:
base(){cout << "Default constructor\n";}
base(char s[ ]){cout << "Copy constructor: " << s << ".\n";}
};

class der:public base
{
public:
der(char s[ ])
:base(strlen(s)==0 ? "N/A" : s)
{
//if (strlen(s)==0)
// base("N/A");
//else
// base(s);
}
};

int main()
{
int (a);
der d1("");
der d2("sample str");
cin.get();
return 0;
}
Результат такой, как и ожидал автор:
Код:
Copy constructor: N/A.
Copy constructor: sample str.
Записан
Алексей++
кот глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #28 : 31-05-2008 19:23 » 

LifeMaker, он же не правильного вывода хотел добиться, а понять порядок вызовов )
Записан

LifeMaker
Гость
« Ответ #29 : 31-05-2008 19:49 » 

Отдельный вопрос, почему при передаче строки во втором случае вызывается конструктор класса base без параметров. Если создавать именованный объект, то вызывается конструктор base(char s[]). Если неименованный, то конструктор по умолчанию, если не выполнить явное приведение типа base ((char*)str);.

меня четвёртая строка тоже сначала в тупик поставила. вроде по всем правилам должен был base(char s[ ]) сработать.
но потом всё-таки до меня дошло. C++ позволяет при объявлении переменной взять её название в скобки.
Например:
Код:
int (a);
char (*c);
float (m[15]);
Вполне валидные, компилящиеся и работающие объявления. Полностью эквивалентные этому:
Код:
int (a);
char (*c);
float (m[15]);
В данном случае компилятор воспринял строку "base(s);" как объявление локальной переменной типа base с именем s (да, C++ разрешает давать локальным переменным имена совпадающие с именами формальных параметров).
Убедиться, что это действительно так можно изменив код следующим образом:
Код:
#include <string.h>
#include <iostream>
using namespace std;

class base
{
const char* who_am_i;
public:
base()
{
cout << "Default constructor\n";
who_am_i = "I am constructed without parameters\n";
}
base(char s[ ])
{
cout << "Copy constructor.\n";
who_am_i = "I am constructed with char s[] parameter\n";
}
void print_about_yourself()
{
cout << who_am_i;
}
};

class der:public base
{
public:
der(char s[ ])
{
if (strlen(s)==0)
base("N/A");
else
{
base(s);
s.print_about_yourself();
}
}
};

int main()
{
int (a);
der d1("");
der d2("sample str");
cin.get();
return 0;
}
Вывод:
Код:
Default constructor
Copy constructor.
Default constructor
Default constructor
I am constructed without parameters
Записан
Страниц: [1] 2  Все   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines