Вам нужны виртуальные методы для безопасного понижения, простоты и лаконичности.
Это то, что делают виртуальные методы: они безопасно опущены, с явно простым и сжатым кодом, избегая небезопасных ручных отбросов в более сложных и подробный код, который вы в противном случае имели бы.
Следующий код преднамеренно неправильный & rdquo ;. Он не объявляет метод value
как virtual
и поэтому создает непреднамеренное & ldquo; wrong & rdquo; результат, а именно 0:
#include <iostream>
using namespace std;
class Expression
{
public:
auto value() const
-> double
{ return 0.0; } // This should never be invoked, really.
};
class Number
: public Expression
{
private:
double number_;
public:
auto value() const
-> double
{ return number_; } // This is OK.
Number( double const number )
: Expression()
, number_( number )
{}
};
class Sum
: public Expression
{
private:
Expression const* a_;
Expression const* b_;
public:
auto value() const
-> double
{ return a_->value() + b_->value(); } // Uhm, bad! Very bad!
Sum( Expression const* const a, Expression const* const b )
: Expression()
, a_( a )
, b_( b )
{}
};
auto main() -> int
{
Number const a( 3.14 );
Number const b( 2.72 );
Number const c( 1.0 );
Sum const sum_ab( &a, &b );
Sum const sum( &sum_ab, &c );
cout << sum.value() << endl;
}
В строке, прокомментированной как & ldquo; bad & rdquo; вызывается метод Expression::value
, потому что статически известный тип (тип, известный во время компиляции) является Expression
, а метод value
не является виртуальным.
Объявление value
как virtual
в статически известном типе Expression
гарантирует, что каждый вызов будет проверять, каков фактический тип объекта, и вызвать соответствующую реализацию value
для этого динамического типа:
#include <iostream>
using namespace std;
class Expression
{
public:
virtual
auto value() const -> double
= 0;
};
class Number
: public Expression
{
private:
double number_;
public:
auto value() const -> double
override
{ return number_; }
Number( double const number )
: Expression()
, number_( number )
{}
};
class Sum
: public Expression
{
private:
Expression const* a_;
Expression const* b_;
public:
auto value() const -> double
override
{ return a_->value() + b_->value(); } // Dynamic binding, OK!
Sum( Expression const* const a, Expression const* const b )
: Expression()
, a_( a )
, b_( b )
{}
};
auto main() -> int
{
Number const a( 3.14 );
Number const b( 2.72 );
Number const c( 1.0 );
Sum const sum_ab( &a, &b );
Sum const sum( &sum_ab, &c );
cout << sum.value() << endl;
}
Здесь вывод 6.86
, как и должно быть, поскольку виртуальный метод называется виртуальным. Это также называется динамическим связыванием вызовов. Выполняется небольшая проверка, нахождение фактического динамического типа объекта и реализация соответствующего метода для этого динамического типа.
Соответствующая реализация является той, которая относится к наиболее определенному (самому производному) классу.
Обратите внимание, что реализации методов в производных классах здесь не отмечены virtual
, но вместо этого отмечены override
. Они могут быть отмечены virtual
, но они автоматически становятся виртуальными. Ключевое слово override
гарантирует, что если в каком-либо базовом классе есть not такой виртуальный метод, вы получите ошибку (что желательно).
Без virtual
нужно было бы реализовать некоторую Do It Yourself версию динамической привязки. Это обычно подразумевает небезопасное ручное понижение, сложность и многословие.
Для случая одной функции, как здесь, достаточно сохранить указатель на функцию в объекте и вызвать через этот указатель функции, но даже это связано с некоторыми небезопасными понижениями, сложностью и многословием, а именно:
#include <iostream>
using namespace std;
class Expression
{
protected:
typedef auto Value_func( Expression const* ) -> double;
Value_func* value_func_;
public:
auto value() const
-> double
{ return value_func_( this ); }
Expression(): value_func_( nullptr ) {} // Like a pure virtual.
};
class Number
: public Expression
{
private:
double number_;
static
auto specific_value_func( Expression const* expr )
-> double
{ return static_cast<Number const*>( expr )->number_; }
public:
Number( double const number )
: Expression()
, number_( number )
{ value_func_ = &Number::specific_value_func; }
};
class Sum
: public Expression
{
private:
Expression const* a_;
Expression const* b_;
static
auto specific_value_func( Expression const* expr )
-> double
{
auto const p_self = static_cast<Sum const*>( expr );
return p_self->a_->value() + p_self->b_->value();
}
public:
Sum( Expression const* const a, Expression const* const b )
: Expression()
, a_( a )
, b_( b )
{ value_func_ = &Sum::specific_value_func; }
};
auto main() -> int
{
Number const a( 3.14 );
Number const b( 2.72 );
Number const c( 1.0 );
Sum const sum_ab( &a, &b );
Sum const sum( &sum_ab, &c );
cout << sum.value() << endl;
}
. Один положительный способ взглянуть на это - если вы столкнулись с небезопасным понижением, сложностью и многословием, как указано выше, виртуальный метод или методы могут действительно помочь.
unistd.h
и fork
являются частью стандарта POSIX .
Похоже, вы используете gcc как часть MinGW
Похоже, вы используете gcc как часть MinGW
g1], который предоставляет заголовокunistd.h
, но не выполняет такие функции, как fork
. Cygwin делает реализацию реализаций таких функций, как fork
. Однако, поскольку это домашнее задание, у вас уже должны быть инструкции о том, как получить рабочую среду .
Я думаю, что вам нужно сделать следующее:
pid_t pid = fork();
Чтобы узнать больше о Linux API, перейдите на эту страницу онлайн-руководства или даже зайдите в свою терминал прямо сейчас и введите
man fork
Удачи!
Как вы уже отметили, fork () следует определить в unistd.h - по крайней мере, согласно страницам man, которые поставляются с Ubuntu 11.10. Минимальный:
#include <unistd.h>
int main( int argc, char* argv[])
{
pid_t procID;
procID = fork();
return procID;
}
... строит без предупреждений в 11.10.
Говоря о том, какой дистрибутив UNIX / Linux вы используете? Например, я нашел несколько не замечательных функций, которые должны быть определены в заголовках Ubuntu 11.10, нет. Например:
// string.h
char* strtok_r( char* str, const char* delim, char** saveptr);
char* strdup( const char* const qString);
// stdio.h
int fileno( FILE* stream);
// time.h
int nanosleep( const struct timespec* req, struct timespec* rem);
// unistd.h
int getopt( int argc, char* const argv[], const char* optstring);
extern int opterr;
int usleep( unsigned int usec);
Пока они определены в вашей библиотеке C, это не будет большой проблемой. Просто определите свои собственные прототипы в заголовке совместимости и сообщите о стандартных проблемах с заголовками тем, кто поддерживает ваш дистрибутив ОС.
У вас есть #include <unistd.h>
, где объявлено fork()
.
Итак, вам, вероятно, нужно сообщить системе, чтобы показать определения POSIX, прежде чем включать заголовки системы:
#define _XOPEN_SOURCE 600
Вы можете использовать 700, если считаете, что ваша система в основном совместима с POSIX 2008 или даже 500 для более старой системы. Поскольку fork()
существует бесконечно, он будет отображаться с любым из них.
Если вы компилируете с -std=c99 --pedantic
, то все объявления для POSIX будут скрыты, если вы явно не запросите их как показано .
Вы также можете играть с _POSIX_C_SOURCE
, но с помощью _XOPEN_SOURCE
подразумеваются правильные соответствующие _POSIX_C_SOURCE
(и _POSIX_SOURCE
и т. д.).