V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
codechaser
V2EX  ›  C

C++里的`::`作用域运算符和`.`运算符怎么总感觉有点奇怪?

  •  
  •   codechaser · 2019-04-11 20:04:12 +08:00 · 3405 次点击
    这是一个创建于 2084 天前的主题,其中的信息可能已经有所发展或是发生改变。

    各位好!百度了一下也不是太清楚:

    #include <iostream>
    #include <memory>
    using namespace std;
    
    class A {
    public:
    	typedef shared_ptr<A> Ptr;
    	int a = 10;
        void test(){
        	std::cout << a << std::endl;
        }
    };
    int main()
    {
    	A foo;
    	cout << foo.a << endl;
    	A::Ptr bar;
    	//cout << A::a << endl;
    	//foo.Ptr c;
    }
    

    假如这里Ptr被当成类型,它算不算一个实例变量呢?注释的两行都是不可以的。我看很多都是这样写的,那我为什么不在要用Ptr的时候在类外面需要的时候定义?typedef shared_ptr<A> Ptr再用也没错啊。

    18 条回复    2019-04-19 19:41:00 +08:00
    429839446
        1
    429839446  
       2019-04-11 20:14:55 +08:00
    这好像是依赖类型, 典型的像 stl 容器里面的各种类型, ::value_type, ::iterator, ::const_interator 之类, 加了类的命名空间可以防止冲突. 在泛型或者模板元的时候, 可以直接获取这些类型做一些操作. 另外类静态方法和静态变量可以. 访问也可以::访问, 为了显示指出这一点,我一般用后者访问
    across
        2
    across  
       2019-04-11 20:17:51 +08:00 via iPhone
    typedef 相当于增加个别名,Ptr 和 shared_ptr<A>一个意思。
    这么想,反正 . 前面的就是个实例。
    wevsty
        3
    wevsty  
       2019-04-11 20:26:25 +08:00   ❤️ 1
    ::前面的是命名空间的名字,后面可以是类型,也可以是函数。
    .前面是一个具体类型的实例,后面是这个实例对应的成员,成员函数。
    zealot0630
        4
    zealot0630  
       2019-04-11 20:32:49 +08:00
    这是 C++以及众多语言的设计缺陷之一,滥用 static 概念。

    未防止歧义,我用 /代表 . 或者 ::

    1. foo/Ptr 和 bar/Ptr 是同一个东西么?是,所以本质上 Ptr 是属于类的。
    2. foo/a 和 bar/a 是同一个东西么?不是,所以本质上 a 是属于对象的。

    访问类内东西使用 ::
    访问对象内东西使用 .
    GjriFeu
        5
    GjriFeu  
       2019-04-11 20:49:24 +08:00
    @zealot0630 那如果我定义的是 static int a; foo/a 和 bar/a 是同一个东西么?是
    zealot0630
        6
    zealot0630  
       2019-04-11 20:55:40 +08:00 via Android
    是啊 所以要用 类::a 访问
    Yggdroot
        7
    Yggdroot  
       2019-04-11 21:15:32 +08:00
    首先,你要搞清楚,Ptr 是 typedef 的一个类型,不是实例变量,比如可以认为 Ptr 是个 int,这样就不会糊涂了。注释里的 foo.Ptr 这种写法,就像写 foo.int 一样,明显是不对且无意义的。

    > 那我为什么不在要用 Ptr 的时候在类外面需要的时候定义? typedef shared_ptr<A> Ptr 再用也没错啊

    你这样也没错,定义在类里面,只是作用域在类里面,对外面不可见,如果外面想访问,必须指定作用域,也就是 A::Ptr。

    A::a 不可以这样,是因为 a 不是静态变量,静态变量是属于类的,故可以写成 A::a。非静态变量是属于每个实例的,不可以写成 A::a。
    Yggdroot
        8
    Yggdroot  
       2019-04-11 21:19:37 +08:00
    @zealot0630 > 1. foo/Ptr 和 bar/Ptr 是同一个东西么?是,所以本质上 Ptr 是属于类的。

    他们是同一个东西,因为他们都是错误写法。因为 Ptr 是个类型,而不是变量,不存在变量 /类型这种写法。
    eret9616
        9
    eret9616  
       2019-04-11 21:24:46 +08:00
    typedef 在编译阶段将 shared_ptr 加上 Ptr 这个别名

    进入 main 函数开始执行,

    A::Ptr bar;

    这句话是声明变量, 数据机构是类 A 中 Ptr , 在栈上分配,这个变量名是 bar

    A::a 是声明变量 所以不能 cout

    foo 是实例化的对象... 不要和声明搞混啊
    eret9616
        10
    eret9616  
       2019-04-11 21:28:42 +08:00
    另外看不懂楼上们在说什么...
    missdeer
        11
    missdeer  
       2019-04-11 22:20:48 +08:00 via Android
    7 楼正解
    shoujiaxin
        12
    shoujiaxin  
       2019-04-11 22:38:54 +08:00 via iPhone
    @zealot0630 请教一下,为什么 . 和 :: 是设计缺陷呢?以及它们和 static 概念有什么关系?没太搞明白
    GeruzoniAnsasu
        13
    GeruzoniAnsasu  
       2019-04-12 00:20:50 +08:00   ❤️ 2
    hhhhhh 建议暂时先记住用法,这块能展开的东西多了去了

    ::这个符号跟 C++最邪恶晦涩的部分( templates )紧密相关,但简单来说,他只有一个含义,就是“属于 namespace ”

    A::a 这个语法,在 C++里,是 ambiguous 的,唯一能确定的是,a 在 A namespace 中(如果把 class 也看做是 namespace 的话),所以先不要先入为主认为 A::a 一定是表示变量或者类型,实际上 A 也好 a 也好,它们可以是变量,可以是类型,甚至还可以是一个 incomplete type (在类型推导中),一个最令人颤抖的例子是 STO 上关于 C++语言是否是 context-free 的讨论:
    https://stackoverflow.com/questions/14589346/is-c-context-free-or-context-sensitive

    由于 C++的 templates 是 Turing Complete 的,所以,推导 A::a 的 a 到底是什么东西,的这个过程,是 Turing Complete 的,理论上你可以写出一个“推测出 a 是什么东西的时候处理完一次 HTTP 请求”这样的程序

    与之相比,A.a,必定代表 访问 对象 A 的成员 a,a 一定被绑定到实例 A,这个 syntax 是不存在歧义性的



    然后至于为什么要有::这种东西
    这么说,当你不需要用 templates 的时候,它仅仅表示一个名字所属的限定,真的没什么用,你甚至可以手动 A_B_c 这样子从命名上划分 namespace
    但用到 templates 的时候,模板有一个决定性的特性是
    A<特化 1>::v

    A<特化 2>::v
    可以不同,但不同的 v 都可以用同一个 dependent type 去使用,这个时候 A<T>::v 的 v 是什么取决于 T,而 T 是可随着模板实例化位置的上下文不同,会自动改变的!这个时候可以说::的作用是使一个 type 能够依赖于另一个 type,而这也编译期多态的关键



    展开太多估计也看不懂,而且我表达得也很可能不准确。
    四点:
    c++里 value 和 type 永远是俩种东西,不存在 python 中“ class 是 type object ”这么混沌的语义
    c++里::这个运算符仅仅表示 namespace 所属,左右两边到底是 type 还是 value 都有可能
    就你的例子来说 typedef 在外面当然可以,但定义在里面的 dependent type 肯定是有自己作用的
    static 变量其实就是把它所在的 class 看做一个 namespace,它自己是这个 namespace 下的变量,对比一下全局变量和 namespace 中的“全 namespace 变量”就明白了
    wutiantong
        14
    wutiantong  
       2019-04-12 10:01:40 +08:00
    @GeruzoniAnsasu 很深入的回复,其实还可以提一下 ADL。。。
    wutiantong
        15
    wutiantong  
       2019-04-12 10:15:57 +08:00
    学编程还是别“百度一下”了,直接上 https://stackoverflow.com 应该不用翻墙
    codechaser
        16
    codechaser  
    OP
       2019-04-12 22:43:44 +08:00 via Android
    @GeruzoniAnsasu 感谢!因为之前主要用 python 和 java,最近转学 c++,照着写代码总感觉这个"::"充满不解😂
    codechaser
        17
    codechaser  
    OP
       2019-04-12 22:44:13 +08:00 via Android
    @wutiantong 百度一下只是习惯😁
    LGA1150
        18
    LGA1150  
       2019-04-19 19:41:00 +08:00
    @wutiantong 然而 StackOverflow 的图床 Imgur 被墙了
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1045 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:36 · PVG 03:36 · LAX 11:36 · JFK 14:36
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.