Таким образом, почти каждый ответ здесь, кажется, пропустит конкретный момент. Переменные класса никогда не становятся переменными экземпляра, как показано в приведенном ниже коде. Используя метакласс, чтобы перехватить назначение переменных на уровне класса, мы можем видеть, что при переназначении a.myattr метод магизации полей для класса не вызывается. Это связано с тем, что присваивание создает новую переменную экземпляра. Это поведение абсолютно не имеет ничего общего с переменной класса, как показано вторым классом, который не имеет переменных класса и все же допускает присвоение поля.
class mymeta(type):
def __init__(cls, name, bases, d):
pass
def __setattr__(cls, attr, value):
print("setting " + attr)
super(mymeta, cls).__setattr__(attr, value)
class myclass(object):
__metaclass__ = mymeta
myattr = []
a = myclass()
a.myattr = [] #NOTHING IS PRINTED
myclass.myattr = [5] #change is printed here
b = myclass()
print(b.myattr) #pass through lookup on the base class
class expando(object):
pass
a = expando()
a.random = 5 #no class variable required
print(a.random) #but it still works
IN SHORT Переменные класса НИЧЕГО НЕ делают с переменными экземпляра .
Более ясно. Они просто попадают в область поиска по экземплярам. Переменные класса фактически являются переменными экземпляра для самого объекта класса. Вы также можете иметь метаклассивные переменные , если хотите, а также потому, что сами метаклассы тоже являются объектами. Все это объект, независимо от того, используется ли он для создания других объектов или нет, поэтому не следует привязывать семантику использования других слов в классе слов. В python класс - это просто объект, который используется для определения того, как создавать другие объекты и каковы их поведение. Метаклассы - это классы, которые создают классы, чтобы еще раз проиллюстрировать этот момент.
static_cast
- это первый бросок, который вы должны попытаться использовать. Он делает такие вещи, как неявные преобразования между типами (например, int
- float
или указатель на void*
), и он также может вызывать явные функции преобразования (или неявные). Во многих случаях явно не указано static_cast
, но важно отметить, что синтаксис T(something)
эквивалентен (T)something
, и его следует избегать (подробнее об этом позже). Однако T(something, something_else)
является безопасным и гарантированно вызывает конструктор.
static_cast
также может выполняться через иерархии наследования. Это необязательно при бросании вверх (в сторону базового класса), но при отбрасывании вниз его можно использовать до тех пор, пока он не наследует virtual
наследование. Однако он не проверяет, и это неопределенное поведение static_cast
вниз по иерархии к типу, который на самом деле не является типом объекта.
const_cast
может использоваться удалить или добавить const
к переменной; ни один другой C ++-способ не способен удалить его (даже не reinterpret_cast
). Важно отметить, что изменение ранее значения const
не определено, если исходная переменная const
; если вы используете его, чтобы удалить const
ссылку на то, что не было объявлено с помощью const
, это безопасно. Это может быть полезно, например, при перегрузке функций-членов на основе const
. Его также можно использовать для добавления const
к объекту, например, для вызова перегрузки функции-члена.
const_cast
также работает аналогично на volatile
, хотя это менее распространено.
dynamic_cast
используется почти исключительно для обработки полиморфизма. Вы можете наложить указатель или ссылку на любой полиморфный тип на любой другой тип класса (полиморфный тип имеет хотя бы одну виртуальную функцию, объявленную или унаследованную). Вы можете использовать его больше, чем просто бросать вниз - вы можете бросить боком или даже еще одну цепочку. dynamic_cast
будет искать желаемый объект и, если возможно, вернуть его. Если он не может, он вернет nullptr
в случае указателя или выбросит std::bad_cast
в случае ссылки.
dynamic_cast
имеет некоторые ограничения. Это не работает, если в иерархии наследования есть несколько объектов одного типа (так называемый «ужасный бриллиант»), и вы не используете наследование virtual
. Он также может проходить только через наследование наследования - он всегда будет не в состоянии пройти через protected
или private
наследование. Это редко бывает проблемой, так как такие формы наследования встречаются редко.
reinterpret_cast
является самым опасным персонажем и его следует использовать очень экономно. Он превращает один тип непосредственно в другой - например, отбрасывая значение от одного указателя к другому или сохраняя указатель в int
или всевозможные другие неприятные вещи. Во многом, единственная гарантия, которую вы получаете с reinterpret_cast
, состоит в том, что обычно, если вы возвращаете результат обратно к исходному типу, вы получите то же самое значение (но not , если промежуточный тип меньше, чем оригинальный type). Существует ряд преобразований, которые reinterpret_cast
также не может сделать. Он используется в первую очередь для особо странных преобразований и манипуляций с битами, таких как превращение потока необработанных данных в фактические данные или хранение данных в младших битах выровненного указателя.
C-style cast and function- стиль cast - это отливки с использованием (type)object
или type(object)
, соответственно. Листинг C-стиля определяется как первое из следующего, которое преуспевает:
const_cast
static_cast
(хотя и игнорирует ограничения доступа) static_cast
(см. выше), затем const_cast
reinterpret_cast
reinterpret_cast
, затем const_cast
Поэтому он может быть использован в качестве замены для других бросков в некоторых случаях, но может быть чрезвычайно опасным из-за способности переходить в reinterpret_cast
, и последнее должно быть предпочтительным, когда требуется явное литье, если вы не уверены static_cast
, или reinterpret_cast
не удастся. Даже тогда рассмотрим более длинную и более явную опцию.
Приведения в стиле C также игнорируют управление доступом при выполнении static_cast
, что означает, что у них есть возможность выполнить операцию, которую не может выполнять другая акция. Это, в основном, kludge, и, на мой взгляд, это еще одна причина, чтобы избежать приведения в стиле C.
(Много теоретических и концептуальных объяснений приведено выше)
Ниже приведены некоторые практические примеры, когда я использовал static_cast, dynamic_cast, const_cast, reinterpret_cast.
(Также ссылается на это, чтобы понять объяснение: http://www.cplusplus.com/doc/tutorial/typecasting/ )
static_cast:
OnEventData(void* pData)
{
......
// pData is a void* pData,
// EventData is a structure e.g.
// typedef struct _EventData {
// std::string id;
// std:: string remote_id;
// } EventData;
// On Some Situation a void pointer *pData
// has been static_casted as
// EventData* pointer
EventData *evtdata = static_cast<EventData*>(pData);
.....
}
dynamic_cast:
void DebugLog::OnMessage(Message *msg)
{
static DebugMsgData *debug;
static XYZMsgData *xyz;
if(debug = dynamic_cast<DebugMsgData*>(msg->pdata)){
// debug message
}
else if(xyz = dynamic_cast<XYZMsgData*>(msg->pdata)){
// xyz message
}
else/* if( ... )*/{
// ...
}
}
const_cast:
// *Passwd declared as a const
const unsigned char *Passwd
// on some situation it require to remove its constness
const_cast<unsigned char*>(Passwd)
reinterpret_cast:
typedef unsigned short uint16;
// Read Bytes returns that 2 bytes got read.
bool ByteBuffer::ReadUInt16(uint16& val) {
return ReadBytes(reinterpret_cast<char*>(&val), 2);
}
Используйте dynamic_cast
для преобразования указателей / ссылок в иерархию наследования.
Используйте static_cast
для конверсий обычного типа.
Используйте reinterpret_cast
для низкоуровневого переинтерпретации бит. Используйте с особой осторожностью.
Используйте const_cast
для отбрасывания const/volatile
. Избегайте этого, если вы не застряли, используя API-интерфейс с константой.
В дополнение к остальным ответам до сих пор здесь неочевидный пример, где static_cast
недостаточно, чтобы reinterpret_cast
был необходим. Предположим, что есть функция, которая в выходном параметре возвращает указатели на объекты разных классов (которые не имеют общего базового класса). Реальным примером такой функции является CoCreateInstance()
(см. Последний параметр, который фактически является void**
). Предположим, вы запрашиваете определенный класс объекта из этой функции, поэтому заранее знаете тип указателя (который вы часто делаете для COM-объектов). В этом случае вы не можете наложить указатель на указатель на void**
с помощью static_cast
: вам нужно reinterpret_cast<void**>(&yourPointer)
.
В коде:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
//static_cast<void**>(&pNetFwPolicy2) would give a compile error
reinterpret_cast<void**>(&pNetFwPolicy2) );
Однако static_cast
работает для простых указателей (не указателей на указатели), поэтому приведенный выше код можно переписать, чтобы избежать reinterpret_cast
(at цена дополнительной переменной) следующим образом:
#include <windows.h>
#include <netfw.h>
.....
INetFwPolicy2* pNetFwPolicy2 = nullptr;
void* tmp = nullptr;
HRESULT hr = CoCreateInstance(__uuidof(NetFwPolicy2), nullptr,
CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2),
&tmp );
pNetFwPolicy2 = static_cast<INetFwPolicy2*>(tmp);
Это может помочь, если вы знаете немного внутренних ...
static_cast
A*
до B*
всегда, если A и B находятся в иерархии наследования (или void), в противном случае вы получите ошибку компиляции. A&
- B&
. dynamic_cast
(Base*)
до (Derived*)
может выйти из строя, если указатель не является фактически производным типом. A*
на B*
, если приведение недействительно, тогда dynamic_cast вернет nullptr. A&
- B&
, если приведение недействительно, тогда dynamic_cast будет генерировать исключение bad_cast. const_cast
set<T>
, который возвращает только его элементы как const, чтобы убедиться, что вы не меняете его ключ. Однако, если ваше намерение состоит в том, чтобы изменить не-ключевые члены объекта, тогда это должно быть хорошо. Вы можете использовать const_cast для удаления константы. T& foo()
, а также const T& foo()
. Чтобы избежать дублирования кода, вы можете применить const_cast для возврата значения одной функции из другого. reinterpret_cast
Ответил ли этот на ваш вопрос?
Я никогда не использовал reinterpret_cast
, и задаюсь вопросом, работает ли он в случае, который ему нужен, это не запах плохого дизайна. В базе кода я работаю над dynamic_cast
. Разница с static_cast
заключается в том, что dynamic_cast
выполняет проверку времени выполнения, которая может (безопаснее) или не может (больше служебных) быть тем, что вы хотите (см. msdn ).
В то время как другие ответы хорошо описывали все различия между C ++-кастами, я хотел бы добавить короткую заметку, почему вы не должны использовать C-style cast (Type) var
и Type(var)
.
Для начинающих C ++ C -style casts выглядит как операция надмножества над листами C ++ (static_cast & lt;> (), dynamic_cast & lt;> (), const_cast & lt;> (), reinterpret_cast & lt;> ()), и кто-то может предпочесть их по сравнению с C ++-переходами. Фактически, C-стиль является надмножеством и короче для записи.
Основная проблема приведения в стиле C заключается в том, что они скрывают замыслы разработчика в отношении создания. Стили C-стиля могут выполнять практически все типы отливок из обычно безопасных отбрасываний, выполняемых static_cast & lt;> () и dynamic_cast & lt;> (), в потенциально опасные роли, такие как const_cast & lt;> (), где модификатор const может быть удален, поэтому константные переменные могут быть изменены и reinterpret_cast & lt;> (), которые могут даже переинтерпретировать целочисленные значения указателям.
Вот пример.
int a=rand(); // Random number.
int* pa1=reinterpret_cast<int*>(a); // OK. Here developer clearly expressed he wanted to do this potentially dangerous operation.
int* pa2=static_cast<int*>(a); // Compiler error.
int* pa3=dynamic_cast<int*>(a); // Compiler error.
int* pa4=(int*) a; // OK. C-style cast can do such cast. The question is if it was intentional or developer just did some typo.
*pa4=5; // Program crashes.
Основная причина, по которой C ++-роли были добавлены в язык должен был позволить разработчику прояснить его намерения - почему он собирается сделать это. Используя C-стиль, отличные от C ++, вы делаете свой код менее удобочитаемым и больше подвержены ошибкам, особенно для других разработчиков, которые не создали ваш код. Поэтому, чтобы сделать ваш код более читабельным и явным, вы всегда должны предпочитать, что C ++ выполняет броски C-style.
Вот короткая цитата из книги Bjarne Stroustrup (автора C ++). Язык программирования C ++ 4th edition - стр. 302.
Это смещение в стиле C гораздо более опасно, чем именованные операторы преобразования, потому что нотация сложнее обнаружить в большой программе, а тип конверсии, предназначенной программисту, не является явным .
blockquote>