zoukankan      html  css  js  c++  java
  • 从operator new看C++操作符重载

    一、gcc对于new operator的说明

    主要是说明:new操作在语法逻辑上看是在new之后一定要看到“类型”的,举个简单的例子:字面量100是一个常量而不是类型,所以语法 new (100)是语法错误的。

    /* Parse a new-expression.

    new-expression:
    :: [opt] new new-placement [opt] new-type-id new-initializer [opt]
    :: [opt] new new-placement [opt] ( type-id ) new-initializer [opt]

    Returns a representation of the expression. */

    static tree
    cp_parser_new_expression (cp_parser* parser)

    二、new operator对应的语义

    从语义上看,主要是计算出类型的大小,然后调用operator new函数。
    static tree
    build_new_1 (vec<tree, va_gc> **placement, tree type, tree nelts,
    vec<tree, va_gc> **init, bool globally_qualified_p,
    tsubst_flags_t complain)
    {
    ……
    if (!globally_qualified_p
    && CLASS_TYPE_P (elt_type)
    && (array_p
    ? TYPE_HAS_ARRAY_NEW_OPERATOR (elt_type)
    : TYPE_HAS_NEW_OPERATOR (elt_type)))
    {
    ……
    }
    alloc_call = build_new_method_call (build_dummy_object (elt_type),
    fns, placement,
    /*conversion_path=*/NULL_TREE,
    LOOKUP_NORMAL,
    &alloc_fn,
    complain);
    }
    else
    {
    /* Use a global operator new. */
    /* See if a cookie might be required. */
    if (!(array_p && TYPE_VEC_NEW_USES_COOKIE (elt_type)))
    {
    cookie_size = NULL_TREE;
    /* No size arithmetic necessary, so the size check is
    not needed. */
    if (outer_nelts_check != NULL && inner_size.is_one ())
    outer_nelts_check = NULL_TREE;
    }

    alloc_call = build_operator_new_call (fnname, placement,
    &size, &cookie_size,
    outer_nelts_check,
    &alloc_fn, complain);
    }
    ……
    }

    三、gcc如何表示一个operator

    在gcc内部,查找operator new就是查找类型名称为“operator new”的函数,这个是预定义的类型。
    static tree
    cp_parser_operator (cp_parser* parser)
    {
    tree id = NULL_TREE;
    cp_token *token;

    /* Peek at the next token. */
    token = cp_lexer_peek_token (parser->lexer);
    /* Figure out which operator we have. */
    switch (token->type)
    {
    ……
    case CPP_MULT:
    id = ansi_opname (MULT_EXPR);
    break;
    ……
    /* If we have selected an identifier, we need to consume the
    operator token. */
    if (id)
    cp_lexer_consume_token (parser->lexer);
    /* Otherwise, no valid operator name was present. */
    else
    {
    cp_parser_error (parser, "expected operator");
    id = error_mark_node;
    }

    return id;
    }

    其定义为
    #define ansi_opname(CODE)
    (operator_name_info[(int) (CODE)].identifier)

    名字的初始化

    static void
    init_operators (void)
    {
    tree identifier;
    char buffer[256];
    struct operator_name_info_t *oni;

    #define DEF_OPERATOR(NAME, CODE, MANGLING, ARITY, ASSN_P)
    sprintf (buffer, ISALPHA (NAME[0]) ? "operator %s" : "operator%s", NAME);
    identifier = get_identifier (buffer);
    IDENTIFIER_OPNAME_P (identifier) = 1;

    oni = (ASSN_P
    ? &assignment_operator_name_info[(int) CODE]
    : &operator_name_info[(int) CODE]);
    oni->identifier = identifier;
    oni->name = NAME;
    oni->mangled_name = MANGLING;
    oni->arity = ARITY;

    #include "operators.def"
    #undef DEF_OPERATOR

    gcc-4.8.2gcccpoperators.def
    #define DEF_SIMPLE_OPERATOR(NAME, CODE, MANGLING, ARITY)
    DEF_OPERATOR(NAME, CODE, MANGLING, ARITY, 0)
    ……
    /* Memory allocation operators. */
    DEF_SIMPLE_OPERATOR ("new", NEW_EXPR, "nw", -1)
    所以综合的结果就是,这些函数在内部得标准名称(ansi)名称就是sprintf (buffer, ISALPHA (NAME[0]) ? "operator %s" : "operator%s", NAME); 格式化出来的名字,operator new的名称就是这个“operator new”,而他的mangled name为nw。

    四、举栗子


    可以看到,new是一个语法单位,而operator new作为一个整体更像是一个有约定语义的特殊函数名,可以用operator new来动态申请内存,从而可以替换掉对C库malloc的直接调用。
    tsecer@harry: cat -n operator.new.cpp
    1 typedef unsigned long int size_t;
    2
    3
    4 void * malloc(size_t size)
    5 {
    6 return (void*)0;
    7 }
    8
    9 void * operator new(size_t size)
    10 {
    11 return (void*)malloc;
    12 }
    13
    14 void* foo(int size)
    15 {
    16 return new (size);
    17 }
    18
    19 void* bar(int size)
    20 {
    21 return operator new (size);
    22 }
    tsecer@harry: g++ -c operator.new.cpp
    operator.new.cpp: 在函数‘void* foo(int)’中:
    operator.new.cpp:16:19: 错误:expected type-specifier before ‘;’ token
    return new (size);
    ^
    tsecer@harry:

    五、何时查找操作符重载

    在c++标准中说明,算符重载时,必须至少有一个操作数是非内置基本类型
    An operator function shall either be a non-static member function or be a non-member function and have at least one parameter whose type is a class, a reference to a class, an enumeration, or a reference to an enumeration.
    这意味着,在声明的时候如果操作数均为内置类型,则语法上是非法的,否则可以通过重载算符来改变内置类型(例如 5+5的语义),这样任何人之间基本的信任都没有了。
    tsecer@harry: cat -n overload.add.cpp
    1 int operator + (int x, int y)
    2 {
    3 return x * y;
    4 }
    tsecer@harry: LC_ALL=C g++ -c overload.add.cpp
    overload.add.cpp:1:29: error: 'int operator+(int, int)' must have an argument of class or enumerated type
    int operator + (int x, int y)
    ^
    tsecer@harry
    在gcc内部,生成一个新的表达式的函数为build_new_op_1,从函数实现可以看到,是将算符转换为了字符串名称的函数,然后根据第一个参数类型决定是否到第一个参数类型内部查找,然后添加外层声明的操作符,最后加上内置操作符类型。
    static tree
    build_new_op_1 (location_t loc, enum tree_code code, int flags, tree arg1,
    tree arg2, tree arg3, tree *overload, tsubst_flags_t complain)
    {
    ……
    if (code == MODIFY_EXPR)
    {
    code2 = TREE_CODE (arg3);
    arg3 = NULL_TREE;
    fnname = ansi_assopname (code2);
    }
    else
    fnname = ansi_opname (code);
    ……
    /* Add namespace-scope operators to the list of functions to
    consider. */
    add_candidates (lookup_function_nonclass (fnname, arglist, /*block_p=*/true),
    NULL_TREE, arglist, NULL_TREE,
    NULL_TREE, false, NULL_TREE, NULL_TREE,
    flags, &candidates, complain);
    ……
    /* Add class-member operators to the candidate set. */
    if (CLASS_TYPE_P (TREE_TYPE (arg1)))
    ……

    add_builtin_candidates (&candidates, code, code2, fnname, args,
    flags, complain);
    ……

    }

  • 相关阅读:
    Python3 基本数据类型
    C语言使用以及其它语言经常出的错误
    Deleting array elements in JavaScript
    postgresql 导出建表语句的方法-类似describe table
    什么是分布式系统(通俗易懂的说法)
    什么是分布式系统,分布式系统例子?
    【转】Linux cp -a用法
    [转]Go基础之锁的初识
    fabricJs使用系列(一)
    chrome 的onbeforeunload事件没有触发
  • 原文地址:https://www.cnblogs.com/tsecer/p/13068339.html
Copyright © 2011-2022 走看看