跳至主要內容
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?

4563博客

全新的繁體中文 WordPress 網站
  • 首頁
  • 被 C++的语法问题折磨了,有没有大佬帮我解答一下疑惑
未分類
14 10 月 2020

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

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

資深大佬 : rainboat 3

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(Test *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)

  • 資深大佬 : jmc891205

    取址操作符
    指针数组

  • 主 資深大佬 : rainboat

    @jmc891205 按理说 abc 都是类的成员变量,没有对象的话这些变量也是不存在的,为啥可以对其进行取址操作呀

  • 資深大佬 : ysc3839

    https://stackoverflow.com/questions/1929887/is-pointer-to-inner-struct-member-forbidden
    大概是这种用法,具体叫什么我也不知道。

  • 資深大佬 : codehz

    指向成员的指针
    https://en.cppreference.com/w/cpp/language/pointer

  • 資深大佬 : jmc891205

    @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

  • 資深大佬 : jmc891205

    @jmc891205 哦上发过了

  • 主 資深大佬 : rainboat

    @ysc3839
    @codehz
    @jmc891205
    感谢感谢

  • 資深大佬 : GeruzoniAnsasu

    类成员指针是一个偏移量
    被 C++的语法问题折磨了,有没有大佬帮我解答一下疑惑
    被 C++的语法问题折磨了,有没有大佬帮我解答一下疑惑

    输出 1 的原因是只能找到 bool 的 <<重载
    被 C++的语法问题折磨了,有没有大佬帮我解答一下疑惑

    要用类成员指针必须有一个实例对象,以 object -> *ptr 的方式使用

  • 資深大佬 : 52coder

    @GeruzoniAnsasu 正解,cout << &Test::c << endl;不管哪个成员都是 1,跟 v 和 f 函数没关系。

  • 資深大佬 : codehz

    @GeruzoniAnsasu 不一定是偏移量,具体值是实现定义
    考虑一下虚继承,其中任意子类拿出来都要可以->*方式访问

  • 資深大佬 : by73

    > An rvalue of arithmetic, enumeration, pointer, or pointer to member type can be converted to an rvalue of type bool.
    > 来源 https://stackoverflow.com/a/25553119

    难怪会被转成 bool 。。

    偏个题:你这是考语言律师资格证嘛

  • 資深大佬 : GeruzoniAnsasu

    @by73 又是那句老话了 编译器行为学是 c++的一部分,务必品尝 doge

  • 資深大佬 : elfive

    指向成员变量的指针这语法……真的有用到生产环境吗?

    现在有个趋势,感觉会把人搞懵逼的:静态语言开始融合部分动态语言的特点,动态语言也开始慢慢吸收静态语言的部分特点

    像我这种人真不是很能理解……虽然之前的语法规则也都能用

  • 資深大佬 : user8341

    @elfive +1,这种语法的使用场景是什么?

  • 資深大佬 : byaiu

    @elfive 用在一些库的核心部分吧。看过 ns3,它的 attribute 的部分就用了这个,结合了 macro 和 template 实现了类似 qt 的 property 的功能。

  • 資深大佬 : codehz

    @elfive 当然有,甚至很多属于非开源的代码库。。。
    举个例子,根据逆向工程结果(实际上是符号表里直接看到的),mc 基岩版在处理指令注册的时候使用这个方法把类中的成员注册成指令的参数,这样,指令解析程序就可以在解析指令文本的时候把参数填入那个类了,不使用这个方法,你就需要写一大堆包装函数(虽然实质上确实用模板生成了他们)

  • 資深大佬 : zsxzy

    c++ primer 都有讲到

  • 資深大佬 : fps23dot9999

    放心,考研绝 B 不会考

  • 資深大佬 : zzlhr

    看看指针部分吧

  • 資深大佬 : codyfeng

    越写 c++越不敢说自己会 c++

  • 資深大佬 : Akiyu

    追加一些疑问和理解;

    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

    @Akiyu 看上有三张图的那个,你可以尝试 godbolt.org 去编译一下,会发现纯粹是编译器将 &Test::a 转换成了 bool 值。。

  • 資深大佬 : zxCoder

    c++ 永远地神
    代码最乱的一门语言

  • 資深大佬 : codehz

    @Akiyu 别啥都以汇编为导向,c++这玩意实现定义的东西很多,如果按某个特定实现来学,很容易写出不可移植的东西出来(甚至有些编译器升级也会打破原有的假设)
    打印为 1 的原因就是
    1.编译器没有找到合适的 operator<<重载
    2.然后发现存在一条隐式转换规则(空成员指针值变为 false ;所有其他值变为 true )
    3.接着发现它不是空成员指针,就转换成 true
    (注意,这里不是表示实现定义的值不为 0,实际上如果按照偏移量方式实现,那平凡结构体的首个元素就应该是 0,但是它不是空成员指针,所以仍然是 true)
    4.标准约定默认布尔类型输出规则是 true -> 1 false -> 0,所以这里输出 1

  • 資深大佬 : Wirbelwind

    我记得是 offset

    深度探索 C++对象模型也讲过

  • 資深大佬 : QBugHunter

    总觉得这是回的第四种写法。。。。。

  • 資深大佬 : QBugHunter

    题目没太仔细看,我有几个问题
    1.如果 Test 是个类,那成员变量为什么是 public 的?
    2.如果 Test 作为一个结构,为什么不用 Test.a,Test->b 这种写法?
    3.Test 的析构函数里的那行代码有什么意义,一个类的析构函数把成员变量的值设为 0 ?

  • 資深大佬 : codehz

    @QBugHunter #27 有意义啊,就像函数指针有啥意义,为啥不用函数一样,通过指针可以实现间接访问,而计算机科学领域的任何问题都可以通过增加一个间接的中间层来解决(
    总所周知,c++暂时还没有编译期反射这种东西,没法按照名字来访问未知类型的成员,但是有了这个机制,你至少可以让用户传入成员的指针,然后在模板函数中实现对成员的操作。。。
    这种事在 c 的世界也不是没有,那时候通常都是用偏移量( offsetof ),显然直接的偏移量在 c++的世界有诸多水土不和的问题(例如多继承的问题和类型安全的问题),自然要设计一个适合于 c++的”偏移量”解决方案。。。

  • 資深大佬 : QBugHunter

    @codehz
    你是回复我 3 个问题中的那个?

  • 資深大佬 : codehz

    @QBugHunter #29 是回的第四种写法(
    其他三个问题没啥好问的,问就是作者想这样做而已,毕竟目的不是为了可维护性什么的,就为了演示 /实验这个用法。。。
    —
    我猜测你可能把这个用法当作“绕过 private 访问”的用法了,才会有这三个问题(

  • 資深大佬 : Hconk

    @QBugHunter
    1. C++ 允许变量是 public 的,这里的变量就是用 public 修饰。
    2. .和-> 是类的实例化对象使用的,类通过::访问成员。
    3. 本身这段代码就没什么意义,只是用来演示语法的。。析构函数没什么意义。

  • 資深大佬 : QBugHunter

    @codehz
    不是。。。我的意思是这种语法是否有实际用途,也就是说将类的成员设为公有,直接操作类成员而不是通过接口,因为在我看来,公有类成员是非常危险的

  • 資深大佬 : v2Mark

    有意思的题目,考察的还挺细节的

  • 資深大佬 : wutiantong

    @QBugHunter 我倒觉得无脑上 getter setter 那才叫脱裤子放屁,多此一举。。。

  • 資深大佬 : user8341

    编译的时候不知道要访问哪个数据成员,执行的时候才知道。

  • 資深大佬 : codehz

    @QBugHunter #32
    首先,c++的 class 和 struct 根本没有自带立场,只是某些编码规范强加的属性。。。
    其次,直接访问成员的危险性在于破坏了潜在的维护性,但前提是它真的是需要隐藏的实现细节。。。
    然后这个语法也不是不可以在类内部导出成员指针。。。
    最后,这个语法也是另一个层面的“接口”,只不过抽象程度更高,而不具备一般意义上的形式而已。
    相当于提供了一个特定上下文下,设置和读取特定值的方法。
    然后它们都可以被模板化(上下文类由用户编译期指定,用成员函数来指定特定的成员),只要你认同模板的封装的功能,你就应该理解这种语法也有它存在的意义(结合上面的)。。。

  • 資深大佬 : no1xsyzy

    @QBugHunter 如果不需要类似 ORM 反射之类的修改存取过程,那用 getter 和 setter 没什么意义,至于某天突然发现需要了,还可以一键重构。
    另外,这个语法听起来也可以变成指向方法的指针,之后可以 obj->*methp(params) ,不知道有没有理解错……

  • 資深大佬 : ysc3839

    @no1xsyzy
    > 另外,这个语法听起来也可以变成指向方法的指针

    应该是可以的,std::bind 就是类似的写法。

  • 資深大佬 : GeruzoniAnsasu

    @user8341
    @elfive

    https://doc.qt.io/qt-5/signalsandslots.html

  • 資深大佬 : Tony042

    @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

    @Tony042 还是有点关系的,std::bind 也可以应用于指向成员的指针
    std::bind(&Test::a, _1)是可行的(相当于拿到了一个 int &(Test&)的函数)

  • 資深大佬 : littlewing

    果然这辈子都学不会 c++了

  • 資深大佬 : Wirbelwind

    @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);

文章導覽

上一篇文章
下一篇文章

AD

其他操作

  • 登入
  • 訂閱網站內容的資訊提供
  • 訂閱留言的資訊提供
  • WordPress.org 台灣繁體中文

51la

4563博客

全新的繁體中文 WordPress 網站
返回頂端
本站採用 WordPress 建置 | 佈景主題採用 GretaThemes 所設計的 Memory
4563博客
  • Hostloc 空間訪問刷分
  • 售賣場
  • 廣告位
  • 賣站?
在這裡新增小工具