this[5]是做什么的?我是否调用了某种未定义的行为?下面是什么:
std::vector<decltype(this)> foo{this, this + 5u};这有用吗?我想知道指针算法对this的影响是什么。下面是一个测试程序:
#include <iostream>
#include <vector>
struct Foo
{
int n = 1;
void foo()
{
std::vector<decltype(this)> foo{this, this + 5u};
for (decltype(foo.size()) i = 0; i != foo.size(); ++i)
{
std::cout << foo[i]->n << "\n";
}
}
};
int main()
{
Foo{}.foo();
}
/* OUTPUT:
* 1
* 0
*/发布于 2015-03-08 07:06:27
首先,您需要回想一下,指针上的下标本质上是语法糖,旨在使其更容易与数组一起操作。反常的是,它通过对指向此类数组元素的指针执行算术来实现这一点。
因此,给定int array[3]和指针int* ptr = &array[0],ptr[2]是指向array的第三个元素的指针。
由于完全相同的原因,并且由于数组的名称如何衰减为指针,array[2]是指向array的第三个元素的指针。
您甚至可以更改“起点”:给定int* ptr = &array[1],ptr[1]也是指向array的第三个元素的指针,因为您实际上是在编写(array+1+1)。
没有理由不能将相同的逻辑应用于名为this的指针。但是,当且仅当对象被分配为数组的一部分时,它才是定义良好的,并且您不会试图读取超出该数组的界限。
下面是一个例子:
#include <iostream>
struct T
{
int x;
T(int x) : x(x) {};
void bar() { std::cout << x << std::endl; }
void foo() { this[1].bar(); } // or (this+1).bar()
};
int main()
{
T array[4] = { T(0), T(1), T(2), T(3) };
array[0].foo(); // OK, outputs 1
array[1].foo(); // OK, outputs 2
array[2].foo(); // OK, outputs 3
array[3].foo(); // undefined; `this[1]` is the same as `array[4]`, so
// evaluating that pointer has UB, never mind invoking
// bar() through it and printing a member variable!
}下面是相关的标准措辞:
[C++11: 5.2.1/2]:后缀表达式后跟方括号中的表达式是后缀表达式。其中一个表达式应具有“指向T的指针”类型,另一个表达式应具有未限定作用域的枚举或整型。结果是一个“T”类型的左值。“T”类型应为完全定义的对象类型。表达式E1[E2](根据定义)与*((E1)+(E2))相同[注:有关*和+的详细信息,请参阅5.3和5.7,有关数组的详细信息,请参阅8.3.4。-end备注]
[C++11: 5.7/5]:当向指针添加或从指针中减去具有整型的表达式时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向距原始元素的元素偏移量,以便结果数组元素和原始数组元素的下标差等于整数表达式。..
发布于 2015-03-08 07:00:36
C类的非静态成员函数中的this是C * const类型的特殊变量,它指向内存中的类实例"start“。
使用this的指针运算由与普通指针相同的规则定义,因此
this[5]类似于
struct T
{
...
};
T t[10];
t[5]; // this-它正在访问"array“的第6个元素,或者
*(C *)((void *)this + sizeof(C) * 5)一般来说,这对this来说没有任何意义。
在以下代码中:
std::vector<decltype(this)> foo{this, this + 5u};您可以定义C *指针的向量,并使用以下命令对其进行初始化:
{(void *)this, (void *)this + sizeof(C) * 1, (void *)this + sizeof(C) * 2, (void *)this + sizeof(C) * 3, (void *)this + sizeof(C) * 4}一般来说,这也是没有意义的。
如果C是“简单”类型(POD或没有父/派生类),那么当一堆C实例是连续内存时(例如,在数组或vector中),那么您可以通过这种方式访问(this1)相邻元素。
但是如果您的类是复杂层次结构的一部分,那么this[1]可能指向与C类型不对齐的位置,因为在复杂层次结构中,类在内存中以非平凡的方式对齐。
更新:使用示例更新了问题
在此示例中:
struct Foo
{
int n = 1;
void foo()
{
std::vector<decltype(this)> foo{this, this + 5u};
for (decltype(foo.size()) i = 0; i != foo.size(); ++i)
{
std::cout << foo[i]->n << "\n";
}
}
};
int main()
{
Foo{}.foo();
}Foo是POD类型,根据对齐方式的不同,它很可能占用4、8或16个字节的内存,并且是32位或64位系统。让我们假设sizeof(Foo) == 4。
Foo{}将在堆栈上创建Foo的实例。然后将调用foo()方法。
在foo()方法内部,this将指向堆栈内存中Foo实例的开始。假设它是0xAABBCC00。
这一行:
std::vector<decltype(this)> foo{this, this + 5u};将创建Foo *的向量,并用{0xAABBCC00, 0xAABBCC04, 0xAABBCC08, 0xAABBCC0C}初始化它。
然后在foo上迭代
for (decltype(foo.size()) i = 0; i != foo.size(); ++i)
{
std::cout << foo[i]->n << "\n";
}foo[0]是this,所以一切都会好起来的。
foo[1]->n是(Foo *)((void *)0xAABBCC04)->n,简单来说就是*(int *)(Foo *)((void *)0xAABBCC04),因为n被放在Foo结构的开头--这是在读取与this相邻的一些未直接初始化的内存。这种对foo[2]->、foo[3]->、foo[4]->的读取和读取具有未定义的行为,通常会导致读取一些垃圾或分段错误。
发布于 2015-03-08 07:03:44
指针算术假设它在数组上工作。给定任意类型的t数组
t x[6];则x+5等于&x[5]。
因此,this + 5u假设*this是一个至少有5个后续元素的数组(或std::vector)的元素,并给出第六个元素的地址(因为数组索引从零开始)。
如果*this不是后跟至少5个元素的数组中的一个元素,则计算this + 5和任何后续尝试迭代向量上的元素和取消引用指针将具有未定义的行为。
假设你有一个成员函数可以做到这一点;
void Foo::something()
{
std::vector<decltype(this)> foo{this, this + 5u};
for (auto &iter: foo)
iter->ChangeMe(); // member function of Foo that changes something
}然后像这样使用它
Foo x[6];
x[0].something(); // okay ... will iterate over all elements of x
x[5].something(); // undefined behaviour, since the loop will fall off the end of x它是否有用取决于你在做什么。我建议不要..。很少有对象需要访问包含它的数组(或容器)的元素。
https://stackoverflow.com/questions/28920949
复制相似问题