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

2019-04-11 20:04:12 +08:00
 codechaser

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

#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再用也没错啊。

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

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

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

访问类内东西使用 ::
访问对象内东西使用 .
GjriFeu
2019-04-11 20:49:24 +08:00
@zealot0630 那如果我定义的是 static int a; foo/a 和 bar/a 是同一个东西么?是
zealot0630
2019-04-11 20:55:40 +08:00
是啊 所以要用 类::a 访问
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
2019-04-11 21:19:37 +08:00
@zealot0630 > 1. foo/Ptr 和 bar/Ptr 是同一个东西么?是,所以本质上 Ptr 是属于类的。

他们是同一个东西,因为他们都是错误写法。因为 Ptr 是个类型,而不是变量,不存在变量 /类型这种写法。
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
2019-04-11 21:28:42 +08:00
另外看不懂楼上们在说什么...
missdeer
2019-04-11 22:20:48 +08:00
7 楼正解
shoujiaxin
2019-04-11 22:38:54 +08:00
@zealot0630 请教一下,为什么 . 和 :: 是设计缺陷呢?以及它们和 static 概念有什么关系?没太搞明白
GeruzoniAnsasu
2019-04-12 00:20:50 +08:00
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
2019-04-12 10:01:40 +08:00
@GeruzoniAnsasu 很深入的回复,其实还可以提一下 ADL。。。
wutiantong
2019-04-12 10:15:57 +08:00
学编程还是别“百度一下”了,直接上 https://stackoverflow.com 应该不用翻墙
codechaser
2019-04-12 22:43:44 +08:00
@GeruzoniAnsasu 感谢!因为之前主要用 python 和 java,最近转学 c++,照着写代码总感觉这个"::"充满不解😂
codechaser
2019-04-12 22:44:13 +08:00
@wutiantong 百度一下只是习惯😁
LGA1150
2019-04-19 19:41:00 +08:00
@wutiantong 然而 StackOverflow 的图床 Imgur 被墙了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/554222

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX