Что можно сделать в c, но не С++?
Каковы вещи, которые можно сделать в C, но не на С++, и какие из этих функций вы пропускаете больше всего при кодировании на С++?
Немногие вещи, о которых я могу думать:
- Мы можем назначить любой тип указателя на указатель void без добавления в c, но не в С++.
- Объявлять имена переменных, которые являются ключевыми словами в С++, но не C;)
EDIT: Спасибо @sbi за указание:
1. должно быть: мы можем назначить указатель void любому типу указателя в C, но не в С++
Ответы
Ответ 1
Примечание. Думаю, я поплачу за это, но тогда это вопрос на С++ для разработчиков С++, поэтому...
Каковы вещи, которые можно сделать в C, но не на С++, и какие из этих функций вы пропускаете больше всего при кодировании на С++?
Как разработчик С++, я ничего не пропускаю от C, будь то C99 или иначе.
Я не пишу это просто из злости. Это вопрос для разработчиков на С++, которые пропускают некоторые функции C/C99, потому что игнорируют основные возможности С++. Я действительно верю, что вопрос и его ответы игнорируют жизнеспособные или лучшие альтернативы в С++ (и нет, комментарий "С++ vector - отвратительный" - просто ложная причина).
Вот почему я буду обсуждать здесь каждую из предполагаемых "недостающих функций"...
Массивы переменной длины?
Массивы переменной длины - это языковая функция C99. Его основными преимуществами являются:
- распределение в стеке
- переменная длина при создании
- нет необходимости освобождать
Для наиболее распространенных случаев std::vector
выполнит эту работу и будет иметь больше функций. Например, если я ошибаюсь, массив переменной длины имеет следующие недостатки:
- выделение в стеке означает, что вы не можете вернуть VLA из функции, где она была объявлена
- VLA не может быть изменен, что означает, что если он слишком мал, то вы ввернуты
- VLA должен быть объявлен в области прототипа или в блочной области. Он не может быть внешним или статическим. И вы не можете объявить его как член структуры.
- У него не может быть инициализатор
Вектор может быть изменен и может быть возвращен. И с С++ 0x (и ссылками r-значения) вы можете вернуть вектор, используя семантику перемещения, что означает, что не нужен бесполезный временный объект. Вы можете поместить его в struct/class, он может быть extern или static. вы можете инициализировать его значением по умолчанию, содержимым массива, контейнером или с С++ 0x с списком инициализаторов.
И даже после этого, если вы действительно хотите что-то вроде VLA, на С++, средний разработчик С++ может писать векторный векторный контейнер на основе стека. И для этого не потребуется полное обновление языкового комитета.
Просто для удовольствия, я случайно отправил ответ с простой доказательством концепции класса VLA, подобного С++.
С++ вектор - это большая альтернатива, с большим количеством функций. И в редком случае VLA действительно действительно необходимо, его функции могут эмулироваться определяемым пользователем классом.
Литье void *
в T *
?
Что касается приведения любого void *
в другой типизированный указатель, это не является признаком C, отсутствующим в С++: Это выбор метода слабопечатывания и сильной типизации
И это не так, как если бы это было невозможно сделать на С++, так как вы можете сделать это с помощью броска. Точка этого различия заключается в уменьшении риска ошибки на языке, где void *
не так полезен, как в другом: в моем текущем проекте строк С++ 100k у меня есть нулевые вхождения void *
.
Обозначенные инициализаторы?
Конструкторы предлагают лучшую альтернативу.
Конечно, вы не получаете возможность инициализировать непосредственно данные в структуре, но тогда инкапсуляция данных означает, что большую часть времени данные в моих объектах являются частными, поэтому вся концепция использования назначенных инициализаторы для их инициализации были бы смешными.
Что касается POD-подобных структур, то конструктор легко писать и может обрабатывать случаи, которые назначенные инициализаторы никогда не будут делать (например, инициализация членов с ненулевыми значениями по умолчанию или даже вызывающие функции).
Поскольку С++ фокусируется на инкапсуляции данных, конструкторы предлагают лучшую альтернативу назначенным инициализаторам.
Изменить 2011-11-05:
После повторного чтения этого раздела я хочу уточнить одну точку: назначенные инициализаторы могут быть полезны для очень ограниченных случаев (т.е. POD), что означает, что, хотя я их не пропущу (по просьбе в вопросе), я бы не прочь их иметь.
Составные литералы?
Этот синтаксический сахар снова предполагает, что вы знаете как точную реализацию структуры, так и публичный доступ к ее членам, чего вы обычно хотите избежать в С++.
Еще раз, Compound Literals - это не то, что невозможно обработать функцией, методом или даже конструктором, с преимуществом бонусных функций, как описано выше.
Объявлять имена переменных, являющиеся ключевыми словами в С++, но не C?
Я знаю, как вы себя чувствуете: каждый раз, когда у меня есть возможность использовать interface
, final
или synchronized
в С++, я получаю также Java дрожь...
: - P
Типовой макрос?
Проблема в C состоит в том, что у вас есть довольно много функций, выполняющих одну и ту же семантическую вещь для разных типов, то есть каждая функция должна иметь другое имя. Например, согласно OpenGroup, существуют следующие функции:
-
double sin(double x);
-
float sinf(float x);
-
long double sinl(long double x);
- и др.
Источник: http://www.opengroup.org/onlinepubs/009695399/functions/sin.html
Но их имена - настоящая боль, чтобы помнить, поэтому у кого-то была идея. Что-то о макросе, которое будет использовать встроенное расширение компилятора для вызова правильного в соответствии с типами используемых параметров.
Это все волшебство C99 <tgmath.h>
.
И идея казалась такой awesome, что они даже добавили предложение предложить эту функцию для всех функций в следующем стандарте C, с чем-то как:
#define sin(x) __tgmath(x,,, \
float, sinf, long double, sinl, \
/* etc. */ \
, , sin)(x)
Источник: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1340.htm
Теперь, шокирующие новости: эти функции доступны на С++ с десятилетия: это называется перегрузкой функций.
Например, указанные выше функции объявлены в С++ как:
-
double sin (double x );
-
float sin (float x );
-
long double sin (long double x );
- и др.
Итак, "типичный макрос" - это взломанная реализация, которая стремится (частично) эмулировать более общую перегрузку функций С++.
И угадайте, что: вы можете даже добавить свои собственные перегрузки для своих пользовательских типов.
Заключение
Как показано выше, каждый раз, когда я изучаю функцию C99, вывод заключается в следующем: "Эй, я уже мог сделать это на С++!" (и обычно со словом "лучше" где-то в предложении).
Серьезно, как разработчик С++, то, что я пропустил прямо сейчас, должен использовать С++ 0x на работе. Например, следующие функции С++ 0x:
-
auto
-
constexpr
- инициализированные списки
- ссылки r-value
- лямбды
-
nullptr
- и др.
Все "C недостающие функции из С++" - это переоцененная концепция, которая, я подозреваю, более интересна разработчикам C (и разработчикам C-классов), чем разработчикам на С++.
Ответ 2
Вы можете найти веб-страницу несовместимость между ISO C и ISO С++.
В основном я пропускаю несколько функций C99, которые не находятся в С++:
- Составные литералы;
- Назначенные инициализаторы;
- Переменные макросы аргументов (включены в С++ 0X).
Ответ 3
Не функция C, но, возможно, функция убийцы C состоит в том, что простой синтаксис C89 упрощает запись компилятора. Ну, по сравнению с компилятором С++.
Ответ 4
- Мы можем назначить любой тип указателя на указатель void без добавления в c, но не в С++.
Любой указатель конвертируется в void*
в С++. Вы теряете информацию таким образом, и компилятор не остановит вас от этого. И наоборот, проблема, потому что таким образом вы получаете информацию, которую компилятор не может проверить.
Я думаю, что C разрешает это, в то время как С++ определенно не делает.
Ответ 5
Если мы проигнорируем очевидный источник различий - C99 - ограничившись C89/90, а также отбросим тривиальные варианты, такие как ключевые слова С++, все еще будут некоторые различия между C и С++
(1) Вы уже упоминали о возможности преобразования void *
в любой конкретный тип указателя без трансляции.
(2) Типы функций с "неуказанными" параметрами, т.е. ()
в объявлении типа функции. В C вы можете сделать это
void foo(int, int);
void bar(double);
int main() {
void (*pf)();
pf = foo;
pf(1, 2); /* valid call */
pf = bar;
pf(5.0); /* valid call */
}
Это невозможно в С++. Разумеется, можно также сказать, что общие непротиворечивые объявления функций являются признаком C, которого нет в С++ (относится и к C99).
(3) Некоторые отличия в инициализации массива со строковыми литералами: trailing \0
разрешено отпадать в C, но не в С++
char str[2] = "ab"; /* valid C, not valid C++ */
(4) Ориентировочные определения в C, хотя они в основном не имеют никакого значения.
(5) Еще одна несущественная "особенность": в C вы можете использовать возвращающие значение функции, которые "забывают", чтобы фактически вернуть что-либо
int foo() {
}
Код является законным как в C, так и в С++, но в С++ такая функция безоговорочно произведет поведение undefined. В C функция создавала бы поведение undefined только в том случае, если вы действительно пытались использовать возвращаемое значение
foo(); /* fine in C, undefined behavior in C++ */
(6) Некоторые другие вещи, которые я добавлю позже, если я его запомню.
Ответ 6
У вас может быть массив переменной длины в C, но не на С++. Я считаю, что это будет очень полезно, вместо того чтобы делать new[]
для этого.
Ответ 7
В C вы можете неявно конвертировать между указателями void и другими указателями, но вы должны сделать явное преобразование в С++.
void* void_ptr;
int* int_ptr;
int_ptr = void_ptr; // Invalid in C++, but not in C
void_ptr = int_ptr; // Valid in C and C++
void_ptr = (void*)int_ptr; // Valid in C and C++
int_ptr = (int*)void_ptr; // Valid in C and C++
Ответ 8
Есть несколько тонких различий в синтаксическом сахаре и злоупотреблении типом, но они тривиальны для работы.
Самая важная возможность C - создавать полностью свободностоящие программы без каких-либо внешних зависимостей. Вот почему ядра операционной системы почти повсеместно написаны на C. C. Фактически он предназначен для реализации операционных систем. Можно написать ядра ОС в ограниченном подмножестве С++, но принудительное выполнение этих ограничений происходит только во время соединения, если это вообще так, поэтому гораздо больнее иметь дело с малыми различиями синтаксиса.
Ответ 9
Что мне нравится в C, так это способность говорить о чем-то вроде a = b;
и точно знать, что он делает. В С++ любой может переопределить операторы, что означает, что простой оператор вроде этого может вызвать некоторый массивный конструктор копий (или, что еще хуже, что-то совершенно неуместное). Вы видите a = b;
в С++, вы должны угадать (или пойти и искать), сделал ли кто-то это просто раздражение.
Ответ 10
Как поклонник C, я думаю, что в концепции стиля кодирования может быть и другое, и это будет функциональное программирование в отличие от объектно-ориентированного программирования, известного как ООП! для тех, кто пишет свои коды как statemachines, это очень интересная концепция! В качестве хорошего примера рассмотрим opengl. Кроме того, скорость выполнения кода очень хороша в C из-за меньших ссылок на память. Вам также может понравиться, что многие программисты любят писать свои коды на C из-за простоты дизайна и того, как они используются для него (возможно, это не так просто в синтаксисе). О, и когда вы хотите писать коды очень близко к аппаратным уровням, вы должны использовать чистый C.
Ответ 11
Одной из пропущенных функций C99 является VLA, для которой С++ не должен иметь эквивалента.
Некоторые даже поставили под сомнение возможность написания в С++ объекта, подобного VLA.
Вот почему я добавил этот ответ: несмотря на то, что он немного вышел из темы, он по-прежнему демонстрирует, что с помощью правильной библиотеки разработчик С++ все же может иметь доступ к объектам, имитирующим функции C99. И, таким образом, эти функции не так пропущены, как полагали.
Основной код:
#include <iostream>
#include <string>
#include "MyVLA.hpp"
template <typename T>
void outputVLA(const std::string & p_name, const MyVLA<T> & p_vla)
{
std::cout << p_name << "\n MyVla.size() : ["
<< p_vla.size() << "]\n" ;
for(size_t i = 0, iMax = p_vla.size(); i < iMax; ++i)
{
std::cout << " [" << i << "] : [" << p_vla[i] << "]\n" ;
}
}
int main()
{
{
MY_VLA(vlaInt, 5, int) ;
outputVLA("vlaInt: Before", vlaInt) ;
vlaInt[0] = 42 ;
vlaInt[1] = 23 ;
vlaInt[2] = 199 ;
vlaInt[3] = vlaInt[1] ;
vlaInt[4] = 789 ;
outputVLA("vlaInt: After", vlaInt) ;
}
{
MY_VLA(vlaString, 4, std::string) ;
outputVLA("vlaString: Before", vlaString) ;
vlaString[0] = "Hello World" ;
vlaString[1] = "Wazaabee" ;
vlaString[2] = vlaString[1] ;
vlaString[3] = "Guess Who ?" ;
outputVLA("vlaString: After", vlaString) ;
}
}
Как вы видите, объект MyVLA знает свой размер (что намного лучше, чем использование оператора sizeof
на VLA C99).
И, конечно же, класс MyVLA ведет себя как массив и инициализируется значением size_t (которое может меняться во время выполнения). Единственный сбой связан с характером функции alloca()
, то есть конструктор должен использоваться только косвенно через макрос MY_VLA:
Ниже, код для класса, в файле MyVLA.hpp
#include <alloca.h>
template <typename T>
class MyVLA
{
public :
MyVLA(T * p_pointer, size_t p_size) ;
~MyVLA() ;
size_t size() const ;
const T & operator[] (size_t p_index) const ;
T & operator[] (size_t p_index) ;
private :
T * m_begin ;
T * m_end ;
} ;
#define MY_VLA(m_name, m_size, m_type) \
m_type * m_name_private_pointer = static_cast<m_type *>(alloca(m_size * sizeof(m_type))) ; \
MyVLA<m_type> m_name(m_name_private_pointer, m_size)
template <typename T>
inline MyVLA<T>::MyVLA(T * p_pointer, size_t p_size)
{
m_begin = p_pointer ;
m_end = m_begin + p_size ;
for(T * p = m_begin; p < m_end; ++p)
{
new(p) T() ;
}
}
template <typename T>
inline MyVLA<T>::~MyVLA()
{
for(T * p = m_begin; p < m_end; ++p)
{
p->~T() ;
}
}
template <typename T>
inline size_t MyVLA<T>::size() const
{
return (m_end - m_begin) ;
}
template <typename T>
inline const T & MyVLA<T>::operator[] (size_t p_index) const
{
return *(m_begin + p_index) ;
}
template <typename T>
inline T & MyVLA<T>::operator[] (size_t p_index)
{
return *(m_begin + p_index) ;
}
Макрос - это беспорядок, и, вероятно, он может быть лучше написан. Сам класс, вероятно, не исключение, но это может быть сделано. Во всяком случае, потребуется больше кода для использования (т.е. Обрабатывать копию/назначение, делать новые/удалять приватными, добавляя const
, где это возможно, и т.д.). Я предполагаю, что конец не оправдывает время, которое я потратил на это. Таким образом, это останется доказательством концепции.
Точка "С++ может эмулировать VLA C99, и она может работать даже как массив объектов С++!", мне кажется, мне удалось это продемонстрировать.
Я разрешаю читателю скопировать-вставить компиляцию кода, чтобы увидеть результаты (я скомпилировал его на g++ 4.4.3, на Ubuntu 10.04.
Ответ 12
В C вы можете определить имя переменной для удаления. Вы не можете сделать это на С++.
Ответ 13
Основное различие между C и С++ заключается в том, что С++ является объектно-ориентированным, а C - функцией или процедурой. Парадигма объектно-ориентированного программирования сосредоточена на написании программ, которые являются более читабельными и поддерживаемыми. Это также помогает повторно использовать код, упаковывая группу похожих объектов или используя концепцию модели программирования компонентов. Это помогает логически мыслить, используя концепцию реальных понятий объектов, наследования и полиморфизма. Следует отметить, что есть также некоторые недостатки таких функций. Например, использование полиморфизма в программе может замедлить производительность этой программы.
С другой стороны, функциональное и процедурное программирование фокусируется прежде всего на действиях и событиях, а модель программирования фокусируется на логических утверждениях, которые запускают выполнение программного кода.