被 C++的语法问题折磨了,有没有大佬帮我解答一下疑惑 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
rainboat
V2EX    C++

被 C++的语法问题折磨了,有没有大佬帮我解答一下疑惑

  •  
  •   rainboat 2020-10-25 22:32:01 +08:00 6089 次点击
    这是一个创建于 1863 天前的主题,其中的信息可能已经有所发展或是发生改变。
    class Test{ public: int a, b, c; Test(int i){ a = i; b = c = 0; } ~Test(){ a = b = c = 0; } }; int Test::*v[] = {&Test::a, &Test::b, &Test::c}; void f(est *a, int Test::*b, int c){ a->*b = c; } int main(){ // 输出结果为 1 cout << &Test::c << endl; } 

    考研刷题时碰到的一道题中的一部分代码,我知道 Test::是类的作用域,但是这个&Test::c是个什么东西,还有这个int Test::*v[] = {&Test::a, &Test::b, &Test::c}又是一个什么神奇的数组,翻了半天书也没看出个所以然,上网查也不知道用什么关键词,有没有懂的大佬能帮我一下。

    43 条回复    2020-10-27 13:32:54 +08:00
    jmc891205
        1
    jmc891205  
       2020-10-25 22:38:41 +08:00
    取址操作符
    指针数组
    rainboat
        2
    rainboat  
    OP
       2020-10-25 22:50:33 +08:00 via Android
    @jmc891205 按理说 abc 都是类的成员变量,没有对象的话这些变量也是不存在的,为啥可以对其进行取址操作呀
    ysc3839
        3
    ysc3839  
       2020-10-25 22:51:04 +08:00
    codehz
        4
    codehz  
       2020-10-25 22:52:26 +08:00   2
    jmc891205
        5
    jmc891205  
       2020-10-25 22:56:52 +08:00
    @rainboat

    A pointer to non-static member object m which is a member of class C can be initialized with the expression &C::m exactly. Expressions such as &(C::m) or &m inside C's member function do not form pointers to members.

    https://en.cppreference.com/w/cpp/language/pointer#Pointers_to_data_members
    Akiyu
        21
    Akiyu  
       2020-10-26 11:40:54 +08:00
    追加一些疑问和理解;

    Test t;
    f(&t, &Test::a, 10);
    cout << "------------------" << endl;
    cout << &Test::a << endl;
    cout << "------------------" << endl

    从上面代码汇编后的结果可以知 &Test::a 实际是 Test 中 a 的偏移.
    其中 a 为 Test 首元素, 值为 0 (这点可以通过调用 f 函数时的第二个参数值获得).
    但是为什么 &Test::a 打印后的值为 1 (值为 0 的话应该是 false. 或 0).
    并且这个值在参数传递时固定为 1 (无论是 &Test::a, 还是 &Test::b, 或者 &Test::c. 甚至成员函数).

    关于值为何为 1 我的理解是: 这些值实际的地址不可能是 0. 即使 &Test::a 为 0. 代表其偏移为 0. 但和实际的对象结合后, 其真实地址不可能为 0. 或许编译器对此做了优化.

    后续:
    我试过取成员函数(普通 /virtual, cv 限定, 我未定义但编译器自动生成的函数)地址. 都可以获取地址. 但其构造和析构无法获取. 关于构造为何无法获取. 我同意这个答案:
    https://softwareengineering.stackexchange.com/questions/245613/why-doesnt-c-allow-you-to-take-the-address-of-a-constructor
    但是关于析构为何无法获取. 我未得到满意的答案. 有人知道么?
    by73
        22
    by73  
       2020-10-26 12:05:55 +08:00 via Android
    @Akiyu 看楼上有三张图的那个,你可以尝试 godbolt.org 去编译一下,会发现纯粹是编译器将 &Test::a 转换成了 bool 值。。
    zxCoder
        23
    zxCoder  
       2020-10-26 12:20:47 +08:00
    c++ 永远地神
    代码最乱的一门语言
    codehz
        24
    codehz  
       2020-10-26 12:23:48 +08:00 via Android   3
    @Akiyu 别啥都以汇编为导向,c++这玩意实现定义的东西很多,如果按某个特定实现来学,很容易写出不可移植的东西出来(甚至有些编译器升级也会打破原有的假设)
    打印为 1 的原因就是
    1.编译器没有找到合适的 operator<<重载
    2.然后发现存在一条隐式转换规则(空成员指针值变为 false ;所有其他值变为 true )
    3.接着发现它不是空成员指针,就转换成 true
    (注意,这里不是表示实现定义的值不为 0,实际上如果按照偏移量方式实现,那平凡结构体的首个元素就应该是 0,但是它不是空成员指针,所以仍然是 true)
    4.标准约定默认布尔类型输出规则是 true -> 1 false -> 0,所以这里输出 1
        25
    Wirbelwind  
       2020-10-26 13:47:34 +08:00
    我记得是 offset

    深度探索 C++对象模型也讲过
    QBugHunter
        26
    QBugHunter  
       2020-10-26 13:59:01 +08:00
    总觉得这是回的第四种写法。。。。。
    QBugHunter
        27
    QBugHunter  
       2020-10-26 14:11:36 +08:00
    题目没太仔细看,我有几个问题
    1.如果 Test 是个类,那成员变量为什么是 public 的?
    2.如果 Test 作为一个结构,为什么不用 Test.a,Test->b 这种写法?
    3.Test 的析构函数里的那行代码有什么意义,一个类的析构函数把成员变量的值设为 0 ?
    codehz
        28
    codehz  
       2020-10-26 16:34:15 +08:00   1
    @QBugHunter #27 有意义啊,就像函数指针有啥意义,为啥不用函数一样,通过指针可以实现间接访问,而计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决(
    总所周知,c++暂时还没有编译期反射这种东西,没法按照名字来访问未知类型的成员,但是有了这个机制,你至少可以让用户传入成员的指针,然后在模板函数中实现对成员的操作。。。
    这种事在 c 的世界也不是没有,那时候通常都是用偏移量( offsetof ),显然直接的偏移量在 c++的世界有诸多水土不和的问题(例如多继承的问题和类型安全的问题),自然要设计一个适合于 c++的"偏移量"解决方案。。。
    QBugHunter
        29
    QBugHunter  
       2020-10-26 16:36:40 +08:00
    @codehz
    你是回复我 3 个问题中的那个?
    codehz
        30
    codehz  
       2020-10-26 16:46:52 +08:00
    @QBugHunter #29 是回的第四种写法(
    其他三个问题没啥好问的,问就是作者想这样做而已,毕竟目的不是为了可维护性什么的,就为了演示 /实验这个用法。。。
    --
    我猜测你可能把这个用法当作“绕过 private 访问”的用法了,才会有这三个问题(
    Hconk
        31
    Hconk  
       2020-10-26 16:50:35 +08:00
    @QBugHunter
    1. C++ 允许变量是 public 的,这里的变量就是用 public 修饰。
    2. .和-> 是类的实例化对象使用的,类通过::访问成员。
    3. 本身这段代码就没什么意义,只是用来演示语法的。。析构函数没什么意义。
    QBugHunter
        32
    QBugHunter  
       2020-10-26 17:01:56 +08:00
    @codehz
    不是。。。我的意思是这种语法是否有实际用途,也就是说将类的成员设为公有,直接操作类成员而不是通过接口,因为在我看来,公有类成员是非常危险的
    v2Mark
        33
    v2Mark  
       2020-10-26 17:07:43 +08:00
    有意思的题目,考察的还挺细节的
    wutiantong
        34
    wutiantong  
       2020-10-26 17:30:53 +08:00
    @QBugHunter 我倒觉得无脑上 getter setter 那才叫脱裤子放屁,多此一举。。。
    9LCRwvU14033RHJo
        35
    9LCRwvU14033RHJo  
       2020-10-26 17:37:42 +08:00
    编译的时候不知道要访问哪个数据成员,执行的时候才知道。
    codehz
        36
    codehz  
       2020-10-26 17:52:56 +08:00
    @QBugHunter #32
    首先,c++的 class 和 struct 根本没有自带立场,只是某些编码规范强加的属性。。。
    其次,直接访问成员的危险性在于破坏了潜在的维护性,但前提是它真的是需要隐藏的实现细节。。。
    然后这个语法也不是不可以在类内部导出成员指针。。。
    最后,这个语法也是另一个层面的“接口”,只不过抽象程度更高,而不具备一般意义上的形式而已。
    相当于提供了一个特定上下文下,设置和读取特定值的方法。
    然后它们都可以被模板化(上下文类由用户编译期指定,用成员函数来指定特定的成员),只要你认同模板的封装的功能,你就应该理解这种语法也有它存在的意义(结合上面的)。。。
    no1xsyzy
        37
    no1xsyzy  
       2020-10-26 20:36:59 +08:00
    @QBugHunter 如果不需要类似 ORM 反射之类的修改存取过程,那用 getter 和 setter 没什么意义,至于某天突然发现需要了,还可以一键重构。
    另外,这个语法听起来也可以变成指向方法的指针,之后可以 obj->*methp(params) ,不知道有没有理解错……
    ysc3839
        38
    ysc3839  
       2020-10-26 21:10:44 +08:00 via Android
    @no1xsyzy
    > 另外,这个语法听起来也可以变成指向方法的指针

    应该是可以的,std::bind 就是类似的写法。
    GeruzoniAnsasu
        39
    GeruzoniAnsasu  
       2020-10-26 21:58:38 +08:00
    Tony042
        40
    Tony042  
       2020-10-26 22:16:42 +08:00
    @ysc3839 这个是 class member pointer 和 class member function pointer,是可以指向方法的,std::bind 和这个关系不太大,bind 对 function,function like,和 member function pointer 做了偏特化,我感觉用起来区别还是很大的,bind 主要是用来固定和调整某些 function parameter,class member function pointer 这东西就是 C++黑科技之一,有用到的时候,但用的情况不是很多
    codehz
        41
    codehz  
       2020-10-26 23:00:42 +08:00 via Android
    @Tony042 还是有点关系的,std::bind 也可以应用于指向成员的指针
    std::bind(&Test::a, _1)是可行的(相当于拿到了一个 int &(Test&)的函数)
    littlewing
        42
    littlewing  
       2020-10-26 23:29:14 +08:00
    果然这辈子都学不会 c++了
    Wirbelwind
        43
    Wirbelwind  
       2020-10-27 13:32:54 +08:00
    @Tony042

    x64 上,对成员函数,可以改变函数写法
    class.func(a,b,c); -> func_converted(&class,a,b,c);
    std::mem_fun (原函数最多两个参数,否则不能编译)

    对成员变量,通过上面的方法可以拿到对应的成员变量
    auto interface = std::mem_fn(&class::data);
    interface(&class);
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2935 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 34ms UTC 13:58 PVG 21:58 LAX 05:58 JFK 08:58
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86