Python源码分析:类的机制

-- Illustrations by Daniel Liang --

作者:小屋子大侠,目前主要从事于python后端相关工作,使用使用python大概2年半的时间,平常喜欢分析工作中使用的工具的源码,如supervisor,gunicorn,django等,并编写了相应的源码分析博客,除了使用工具的分析外,对python的底层实现也有相应兴趣并编写过python源码有关的博客,目前主要学习于操作系统相关内容。个人博客地址:https://blog.csdn.net/qq_33339479/

本文主要分析Python中类时如何实现的,在Python中,一切都是对象;任何对象都有一个type,都可以通过class属性,一般情况下为type对应于Python源码中的PyTypeType;在Python的类中,都直接或者间接与Object有关联,都是Object的子类,对应Python中PyBaseObjectType。在Python的启动执行流程一文中有介绍,在Python启动的过程中会首先对默认的类型进行初始化,我们就从这里开始分析。

分析 初始化代码如下:

void
_Py_ReadyTypes(void)
{
    if (PyType_Ready(&PyType_Type) < 0)
        Py_FatalError("Can't initialize 'type'");

    if (PyType_Ready(&_PyWeakref_RefType) < 0)
        Py_FatalError("Can't initialize 'weakref'");

    if (PyType_Ready(&PyBool_Type) < 0)
        Py_FatalError("Can't initialize 'bool'");

    if (PyType_Ready(&PyString_Type) < 0)
        Py_FatalError("Can't initialize 'str'");

    if (PyType_Ready(&PyList_Type) < 0)
        Py_FatalError("Can't initialize 'list'");

    if (PyType_Ready(&PyNone_Type) < 0)
        Py_FatalError("Can't initialize type(None)");

    if (PyType_Ready(&PyNotImplemented_Type) < 0)
        Py_FatalError("Can't initialize type(NotImplemented)");
}

这里分析下PyTypeReady(&PyTypeType)初始化过程,首先我们查看下PyType_Type类型的结构:

PyTypeObject PyType_Type = {
    PyObject_HEAD_INIT(&PyType_Type)      //  类型还是PyType_Type
    0,                  /* ob_size */
    "type",                 /* tp_name */
    sizeof(PyHeapTypeObject),       /* tp_basicsize */
    sizeof(PyMemberDef),            /* tp_itemsize */
    (destructor)type_dealloc,       /* tp_dealloc */
    0,                  /* tp_print */
    0,                  /* tp_getattr */
    0,                  /* tp_setattr */
    type_compare,               /* tp_compare */
    (reprfunc)type_repr,            /* tp_repr */
    0,                  /* tp_as_number */
    0,                  /* tp_as_sequence */
    0,                  /* tp_as_mapping */
    (hashfunc)_Py_HashPointer,      /* tp_hash */    // 哈希函数
    (ternaryfunc)type_call,         /* tp_call */    // tp_call函数
    0,                  /* tp_str */
    (getattrofunc)type_getattro,        /* tp_getattro */
    (setattrofunc)type_setattro,        /* tp_setattro */
    0,                  /* tp_as_buffer */
    Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC |
        Py_TPFLAGS_BASETYPE,        /* tp_flags */
    type_doc,               /* tp_doc */
    (traverseproc)type_traverse,        /* tp_traverse */
    (inquiry)type_clear,            /* tp_clear */
    0,                  /* tp_richcompare */
    offsetof(PyTypeObject, tp_weaklist),    /* tp_weaklistoffset */
    0,                  /* tp_iter */
    0,                  /* tp_iternext */
    type_methods,               /* tp_methods */
    type_members,               /* tp_members */
    type_getsets,               /* tp_getset */
    0,                  /* tp_base */
    0,                  /* tp_dict */
    0,                  /* tp_descr_get */
    0,                  /* tp_descr_set */
    offsetof(PyTypeObject, tp_dict),    /* tp_dictoffset */
    0,                  /* tp_init */
    0,                  /* tp_alloc */
    type_new,               /* tp_new */
    PyObject_GC_Del,                /* tp_free */
    (inquiry)type_is_gc,            /* tp_is_gc */
};

由此可见type定义中的tp_base为空,在初始化过程中就会将基类换成Object;我们看完定义后再查看初始化过程:

int
PyType_Ready(PyTypeObject *type)
{
    PyObject *dict, *bases;
    PyTypeObject *base;
    Py_ssize_t i, n;

    if (type->tp_flags & Py_TPFLAGS_READY) {
        assert(type->tp_dict != NULL);
        return 0;
    }
    assert((type->tp_flags & Py_TPFLAGS_READYING) == 0);

    type->tp_flags |= Py_TPFLAGS_READYING;

#ifdef Py_TRACE_REFS
    /* PyType_Ready is the closest thing we have to a choke point
     * for type objects, so is the best place I can think of to try
     * to get type objects into the doubly-linked list of all objects.
     * Still, not all type objects go thru PyType_Ready.
     */
    _Py_AddToAllObjects((PyObject *)type, 0);
#endif

    /* Initialize tp_base (defaults to BaseObject unless that's us) */
    base = type->tp_base;                                       // 获取初始化类型的基类
    if (base == NULL && type != &PyBaseObject_Type) {           // 如果基类为空,并且初始化的类型不为Object
        base = type->tp_base = &PyBaseObject_Type;              // 将初始化类的基类设置成Object
        Py_INCREF(base);
    }

        /* Now the only way base can still be NULL is if type is
         * &PyBaseObject_Type.
         */

    /* Initialize the base class */
    if (base && base->tp_dict == NULL) {                        // 如果基类的属性列表为空
        if (PyType_Ready(base) < 0)                             // 初始化基类
            goto error;
    }

    /* Initialize ob_type if NULL.  This means extensions that want to be
       compilable separately on Windows can call PyType_Ready() instead of
       initializing the ob_type field of their type objects. */
        /* The test for base != NULL is really unnecessary, since base is only
           NULL when type is &PyBaseObject_Type, and we know its ob_type is
           not NULL (it's initialized to &PyType_Type).  But coverity doesn't
           know that. */
    if (type->ob_type == NULL && base != NULL)                 // 如果初始化类型的类型为空,并且基类不为空
        type->ob_type = base->ob_type;                         // 初始化类型的类型设置成基类的类型

    /* Initialize tp_bases */
    bases = type->tp_bases;                                    // 获取初始化类型的基类列表
    if (bases == NULL) {                                       // 如果基类列表为空
        if (base == NULL)                                      // 如果父类为空
            bases = PyTuple_New(0);                            // 基类则生成一个空的元组
        else
            bases = PyTuple_Pack(1, base);                     // 如果基类不为空,生成长度为1的元组,并将base发入其中
        if (bases == NULL)
            goto error;
        type->tp_bases = bases;                                // 将生成的bases设置到初始化类型中
    }

    /* Initialize tp_dict */
    dict = type->tp_dict;                                      // 获取类型的属性列表
    if (dict == NULL) {                                        
        dict = PyDict_New();                                   // 如果属性为空,则生成一个字典,并设置到初始化类型中
        if (dict == NULL)
            goto error;
        type->tp_dict = dict;
    }

    /* Add type-specific descriptors to tp_dict */
    if (add_operators(type) < 0)                               // 给该类型添加描述方法
        goto error;
    if (type->tp_methods != NULL) {
        if (add_methods(type, type->tp_methods) < 0)           // 如果类型方法不为空,则将方法包装后添加到初始化类型中
            goto error;
    }
    if (type->tp_members != NULL) {
        if (add_members(type, type->tp_members) < 0)           // 如果类型成员不为空,则将成员包装后添加到初始化类型中
            goto error;
    }
    if (type->tp_getset != NULL) {
        if (add_getset(type, type->tp_getset) < 0)             
            goto error;
    }

    /* Calculate method resolution order */
    if (mro_internal(type) < 0) {                              // 获取初始化类型的基础列表
        goto error;
    }

    /* Inherit special flags from dominant base */
    if (type->tp_base != NULL)
        inherit_special(type, type->tp_base);                  // 如果基类不为空,则继承基类的方法属性等

    /* Initialize tp_dict properly */
    bases = type->tp_mro;                                      // 获取初始化类型的基础列表
    assert(bases != NULL);    
    assert(PyTuple_Check(bases));
    n = PyTuple_GET_SIZE(bases);
    for (i = 1; i < n; i++) {
        PyObject *b = PyTuple_GET_ITEM(bases, i);              // 依次获取基础列表的值
        if (PyType_Check(b))
            inherit_slots(type, (PyTypeObject *)b);            // 继承相应的方法
    }

    /* Sanity check for tp_free. */
    if (PyType_IS_GC(type) && (type->tp_flags & Py_TPFLAGS_BASETYPE) &&
        (type->tp_free == NULL || type->tp_free == PyObject_Del)) {
            /* This base class needs to call tp_free, but doesn't have
             * one, or its tp_free is for non-gc'ed objects.
             */
        PyErr_Format(PyExc_TypeError, "type '%.100s' participates in "
                 "gc and is a base type but has inappropriate "
                 "tp_free slot",
                 type->tp_name);
        goto error;
    }

    /* if the type dictionary doesn't contain a __doc__, set it from
       the tp_doc slot.
     */
    if (PyDict_GetItemString(type->tp_dict, "__doc__") == NULL) {    // 设置属性的__doc__属性,如果有设置其中,如果没有则设置为空
        if (type->tp_doc != NULL) {
            PyObject *doc = PyString_FromString(type->tp_doc);
            if (doc == NULL)
                goto error;
            PyDict_SetItemString(type->tp_dict, "__doc__", doc);
            Py_DECREF(doc);
        } else {
            PyDict_SetItemString(type->tp_dict,
                         "__doc__", Py_None);
        }
    }

    /* Some more special stuff */
    base = type->tp_base;                                       // 获取基类,如果子类相应的方法为空,则直接将基类方法设置给初始化类型
    if (base != NULL) {
        if (type->tp_as_number == NULL)
            type->tp_as_number = base->tp_as_number;
        if (type->tp_as_sequence == NULL)
            type->tp_as_sequence = base->tp_as_sequence;
        if (type->tp_as_mapping == NULL)
            type->tp_as_mapping = base->tp_as_mapping;
        if (type->tp_as_buffer == NULL)
            type->tp_as_buffer = base->tp_as_buffer;
    }

    /* Link into each base class's list of subclasses */
    bases = type->tp_bases;                                    // 获取初始化类型的基类列表,
    n = PyTuple_GET_SIZE(bases);
    for (i = 0; i < n; i++) {
        PyObject *b = PyTuple_GET_ITEM(bases, i);            
        if (PyType_Check(b) &&
            add_subclass((PyTypeObject *)b, type) < 0)         // 将初始化类型添加到基类中,填充基类子类列表
            goto error;
    }

    /* All done -- set the ready flag */
    assert(type->tp_dict != NULL);
    type->tp_flags =
        (type->tp_flags & ~Py_TPFLAGS_READYING) | Py_TPFLAGS_READY;   // 设置该类型已经初始化完成
    return 0;

  error:
    type->tp_flags &= ~Py_TPFLAGS_READYING;
    return -1;
}

其中,主要经历了五个不中

  1. 设置type信息,基类及基类列表;
  2. 填充tp_dict;
  3. 确定mro;
  4. 从mro列表继承基类属性;
  5. 设置基类的子类列表。 其中我们从初始化完成tp_dict后填充方法开始分析;
/* Add type-specific descriptors to tp_dict */
    if (add_operators(type) < 0)                               // 给该类型添加描述方法
        goto error;
    if (type->tp_methods != NULL) {
        if (add_methods(type, type->tp_methods) < 0)           // 如果类型方法不为空,则将方法包装后添加到初始化类型中
            goto error;
    }
    if (type->tp_members != NULL) {
        if (add_members(type, type->tp_members) < 0)           // 如果类型成员不为空,则将成员包装后添加到初始化类型中
            goto error;
    }
    if (type->tp_getset != NULL) {
        if (add_getset(type, type->tp_getset) < 0)             
            goto error;
    }

查看add_operators(type)方法;

static int
add_operators(PyTypeObject *type)
{
    PyObject *dict = type->tp_dict;
    slotdef *p;
    PyObject *descr;
    void **ptr;

    init_slotdefs();                        // 排序slotdefs数组
    for (p = slotdefs; p->name; p++) {      // 获取slotdefs的值
        if (p->wrapper == NULL)             // 如果包装的函数为空,则直接获取下一个
            continue;
        ptr = slotptr(type, p->offset);     // 转换获取相应的方法
        if (!ptr || !*ptr)
            continue;
        if (PyDict_GetItem(dict, p->name_strobj))   // 如果在属性列表中获取到相应名称,则不覆盖直接下一个
            continue;
        descr = PyDescr_NewWrapper(type, p, *ptr);   // 将该方法生成一个新的描述符
        if (descr == NULL)
            return -1;
        if (PyDict_SetItem(dict, p->name_strobj, descr) < 0)   // 将生成的描述符设置到对应的属性列表中
            return -1;
        Py_DECREF(descr);
    }
    if (type->tp_new != NULL) {
        if (add_tp_new_wrapper(type) < 0)
            return -1;
    }
    return 0;
}

这其中出现了init_slotdefs,这在Python中有一个叫slot,一个slot对应一个方法。其定义如下;

typedef struct wrapperbase slotdef;

struct wrapperbase {
    char *name;                 // 名称
    int offset;                 // 偏移,相对于PyHead-TypeObject
    void *function;             // 包装的函数
    wrapperfunc wrapper;        
    char *doc;                  // 文档
    int flags;        
    PyObject *name_strobj;     // 对应名称转换为字符串对象
};

对于slotdef,提供了多种宏来定义一个slotdef

#define FLSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC, FLAGS) \
    {NAME, offsetof(PyTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \
     PyDoc_STR(DOC), FLAGS}
#define ETSLOT(NAME, SLOT, FUNCTION, WRAPPER, DOC) \
    {NAME, offsetof(PyHeapTypeObject, SLOT), (void *)(FUNCTION), WRAPPER, \
     PyDoc_STR(DOC)}

两个方法的不同就是一个相对于PyTypeObject,一个相对于PyHeapTypeObject,两个偏移量相对偏移的类型不同。其中PyHeapTypeObject定义如下;

typedef struct _heaptypeobject {
    /* Note: there's a dependency on the order of these members
       in slotptr() in typeobject.c . */
    PyTypeObject ht_type;
    PyNumberMethods as_number;
    PyMappingMethods as_mapping;
    PySequenceMethods as_sequence; /* as_sequence comes after as_mapping,
                      so that the mapping wins when both
                      the mapping and the sequence define
                      a given operator (e.g. __getitem__).
                      see add_operators() in typeobject.c . */
    PyBufferProcs as_buffer;
    PyObject *ht_name, *ht_slots;
    /* here are optional user slots, followed by the members. */
} PyHeapTypeObject;

为什么会有这个区别是因为在一个PyTypeObject对象中,对应的操作方法比如nbadd是存放在函数指针tpasnumber中,而这是另外一个结构,无法计算出nbadd相对于PyTypeObject的相对位置,由PyHeapTypeObject可以看出,可以直接计算相对位置。 接下来查看下slotdefs数组;

static slotdef slotdefs[] = {
    SQSLOT("__len__", sq_length, slot_sq_length, wrap_lenfunc,
           "x.__len__() <==> len(x)"),
    /* Heap types defining __add__/__mul__ have sq_concat/sq_repeat == NULL.
       The logic in abstract.c always falls back to nb_add/nb_multiply in
       this case.  Defining both the nb_* and the sq_* slots to call the
       user-defined methods has unexpected side-effects, as shown by
       test_descr.notimplemented() */
    SQSLOT("__add__", sq_concat, NULL, wrap_binaryfunc,
          "x.__add__(y) <==> x+y"),
    SQSLOT("__mul__", sq_repeat, NULL, wrap_indexargfunc,
          "x.__mul__(n) <==> x*n"),
    SQSLOT("__rmul__", sq_repeat, NULL, wrap_indexargfunc,
          "x.__rmul__(n) <==> n*x"),
    ...
    SQSLOT("__getitem__", sq_item, slot_sq_item, wrap_sq_item,
           "x.__getitem__(y) <==> x[y]"),
    ...
    MPSLOT("__getitem__", mp_subscript, slot_mp_subscript,
           wrap_binaryfunc,
           "x.__getitem__(y) <==> x[y]"),
    ...
}

其中,可以发现有不同名的操作,也有同名的操作,对应于同名的操作是如何做选择呢,此时如果是PyTypeObject,此时相对于PyHeapTypeObject,其中asmapping在assequence上面,所有mpsubscript的排序会比sqitem靠前,所以会选择mpsubscript。 此时继续查看initslotdefs代码;

static void
init_slotdefs(void)
{
    slotdef *p;
    static int initialized = 0;

    if (initialized)
        return;
    for (p = slotdefs; p->name; p++) {
        p->name_strobj = PyString_InternFromString(p->name);     // 将名称转换为Python内部字符串
        if (!p->name_strobj)
            Py_FatalError("Out of memory interning slotdef names");
    }
    qsort((void *)slotdefs, (size_t)(p-slotdefs), sizeof(slotdef),
          slotdef_cmp);                                         // 按照slot_cmp函数规则排序
    initialized = 1;
}

然后执行如下代码;

for (p = slotdefs; p->name; p++) {      // 获取slotdefs的值
        if (p->wrapper == NULL)             // 如果包装的函数为空,则直接获取下一个
            continue;
        ptr = slotptr(type, p->offset);     // 转换获取相应的方法
        if (!ptr || !*ptr)
            continue;
        if (PyDict_GetItem(dict, p->name_strobj))   // 如果在属性列表中获取到相应名称,则不覆盖直接下一个
            continue;
        descr = PyDescr_NewWrapper(type, p, *ptr);   // 将该方法生成一个新的描述符
        if (descr == NULL)
            return -1;
        if (PyDict_SetItem(dict, p->name_strobj, descr) < 0)   // 将生成的描述符设置到对应的属性列表中
            return -1;

这里可以看到当需要在tpdict中设置属性时,并不是直接向slotdef直接设置到属性中,而是通过PyDescrNewWrapper生成一个描述符放置在tpdict中,为何要这么设计呢? 我们先查看PyDescrNewWrapper;

PyObject *
PyDescr_NewWrapper(PyTypeObject *type, struct wrapperbase *base, void *wrapped)
{
    PyWrapperDescrObject *descr;

    descr = (PyWrapperDescrObject *)descr_new(&PyWrapperDescr_Type,
                         type, base->name);
    if (descr != NULL) {
        descr->d_base = base;                // slotdef 
        descr->d_wrapped = wrapped;          // 被包装的函数
    }
    return (PyObject *)descr;
}

继续查看descr_new;

static PyDescrObject *
descr_new(PyTypeObject *descrtype, PyTypeObject *type, const char *name)
{
    PyDescrObject *descr;

    descr = (PyDescrObject *)PyType_GenericAlloc(descrtype, 0);
    if (descr != NULL) {
        Py_XINCREF(type);
        descr->d_type = type;        // 设置装饰的类型
        descr->d_name = PyString_InternFromString(name);  // 设置被装饰的名称
        if (descr->d_name == NULL) {
            Py_DECREF(descr);
            descr = NULL;
        }
    }
    return descr;
}

由此查看PyDescrObject类型;

#define PyDescr_COMMON \
    PyObject_HEAD \
    PyTypeObject *d_type; \
    PyObject *d_name

typedef struct {
    PyDescr_COMMON;
    struct wrapperbase *d_base;
    void *d_wrapped; /* This can be any function pointer */
} PyWrapperDescrObject;

由此可知一个descr是一个type类型的对象,为什么要转换为一个type类型呢,当调用tpdict的方法时,需要调用其中tpcall方法,而slotdef并不是一个可调用对象,并不符合可调用的要求。 在以上流程中,比较重要的就是怎样获取对应的ptr然后生成wrapper存入tp_dict中,

ptr = slotptr(type, p->offset);     // 转换获取相应的方法

对应的方法为

static void **
slotptr(PyTypeObject *type, int ioffset)
{
    char *ptr;
    long offset = ioffset;

    /* Note: this depends on the order of the members of PyHeapTypeObject! */
    assert(offset >= 0);
    assert((size_t)offset < offsetof(PyHeapTypeObject, as_buffer));
    if ((size_t)offset >= offsetof(PyHeapTypeObject, as_sequence)) {
        ptr = (char *)type->tp_as_sequence;
        offset -= offsetof(PyHeapTypeObject, as_sequence);
    }
    else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_mapping)) {
        ptr = (char *)type->tp_as_mapping;
        offset -= offsetof(PyHeapTypeObject, as_mapping);
    }
    else if ((size_t)offset >= offsetof(PyHeapTypeObject, as_number)) {
        ptr = (char *)type->tp_as_number;
        offset -= offsetof(PyHeapTypeObject, as_number);
    }
    else {
        ptr = (char *)type;
    }
    if (ptr != NULL)
        ptr += offset;
    return (void **)ptr;
}

此时,从上至下,依次是PyHeapTypeObject对应的assequence,asmapping,asnumber,这是因为offset的偏移是从大到小进行检查,因为assequence的距离是最远的,如果比assequence还小则再检查asmapping,如果比asmapping还小则检查asnumber,这样就可以查找出对应偏移量对应的方法。 此时tpdict属性字典方法填充完成。 当tpdict完成后类继承相关的操作 由于Python中的类的基础是一句传入的参数依次从左至右开始继承;

>>> class A(object):
...     pass
... 
>>> class B(object):
...     pass
... 
>>> A.__mro__
(<class '__main__.A'>, <type 'object'>)
>>> class C(B,A):
...     pass
... 
>>> class D(C,A):
...     pass
... 
>>> D.__mro__
(<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>> D.__bases__
(<class '__main__.C'>, <class '__main__.A'>)
>>> C.__mro__
(<class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <type 'object'>)
>>>

可见D的继承顺序是从左至右,依次继承初始化, 继承的代码执行流程如下;

/* Initialize tp_dict properly */
    bases = type->tp_mro;                                      // 获取初始化类型的基础列表
    assert(bases != NULL);    
    assert(PyTuple_Check(bases));
    n = PyTuple_GET_SIZE(bases);
    for (i = 1; i < n; i++) {
        PyObject *b = PyTuple_GET_ITEM(bases, i);              // 依次获取基础列表的值
        if (PyType_Check(b))
            inherit_slots(type, (PyTypeObject *)b);            // 继承相应的方法
    }

由例子可知,tpmro的第一项是自身,所以i=1开始,依次获取对应的基类,然后执行,inheritslots,该函数主要就是检查基类中对应的方法子类中是否拥有,如果没有拥有则拷贝到子类中对应的方法中去,以此达到继承父类方法的功能,主要是一些方法的拷贝有兴趣可自行查看。 接着就将子类添加到基类的子类列表中

bases = type->tp_bases;                                    // 获取初始化类型的基类列表,
    n = PyTuple_GET_SIZE(bases);
    for (i = 0; i < n; i++) {
        PyObject *b = PyTuple_GET_ITEM(bases, i);            
        if (PyType_Check(b) &&
            add_subclass((PyTypeObject *)b, type) < 0)         // 将初始化类型添加到基类中,填充基类子类列表
            goto error;
    }

其中调用add_subclass时还生成了Python中的引用相关的操作,有兴趣课自行查看。 至此初始化就完成,一个PyTypeObject的初始化工作就已经完成,初始化内置类型的操作流程基本分析完成。

本文环境:Python2.5系列

原文发布于微信公众号 - Python中文社区(python-china)

原文发表时间:2018-06-27

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏开发与安全

算法:静态查找表(Static Search Table)(顺序查找、二分查找、插值查找、斐波纳契查找)

查找表(Search table)是由同一类型的数据元素(或记录)构成的集合。关键字(key)是数据元素中某个数据项的值,又称为键值,用它可以表示一个数据元素,...

25950
来自专栏刘望舒

Java虚拟机(三)垃圾标记算法与Java对象的生命周期

前言 这一节我们来简单的介绍垃圾收集器,并学习垃圾标记的算法:引用计数算法和根搜索算法,为了更好的理解根搜索算法,会在文章的最后介绍Java对象在虚拟机中的...

20060
来自专栏博客园

Core官方DI解析(4)--CallSiteRuntimeResolver

这两个类都在其CallSiteVisitor<TArgument, TResult>基类中

9930
来自专栏恰童鞋骚年

.NET基础拾遗(1)类型语法基础和内存管理基础

在.NET中所有的内建类型都继承自System.Object类型。在C#中,不需要显示地定义类型继承自System.Object,编译器将自动地自动地为类型添...

12220
来自专栏小勇DW3

回过头来看对象的四种状态强软弱虚引用的理解

  在JDK1.2之后,Java对引用的概念进行了扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(W...

11540
来自专栏cmazxiaoma的架构师之路

关于牛客网的JAVA面试题错题总结以及归纳(1)

15530
来自专栏空帆船w

Java的强引用,软引用,弱引用,虚引用及其使用场景

从 JDK1.2 版本开始,Java 把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引...

37820
来自专栏互联网大杂烩

腾讯面试

时间:2017年3月5号 这次面试和之前的面试差不多,一开始聊项目。聊了项目之后就问基础了,项目的话就不说了。

8120
来自专栏向治洪

java的四种引用类型

java的引用分为四个等级:4种级别由高到低依次为:强引用、软引用、弱引用和虚引用。 ⑴强引用(StrongReference) 强引用是使用最普遍的引用。...

20850
来自专栏流媒体人生

ATL源码学习5---集合与枚举接口支持

http://download.csdn.net/source/1690987

8920

扫码关注云+社区

领取腾讯云代金券