C++ 实施限制;这";C++;,因为它涉及危险但功能性强的示例

C++ 实施限制;这";C++;,因为它涉及危险但功能性强的示例,c++,this,C++,This,我知道给定的这个的值不能在编译时确定。我想知道,一旦分配和构造了一个给定的对象,this的值是缓存的,还是每次使用时都会对表达式进行运行时评估?下面是激发我的问题的具体例子。被警告,这违反了每一个OOP原则和保护功能C++支持的目标。 int main() { string s1 = string("I am super a super long string named s1, and won't be SSO"); string s2 = string("I am super

我知道给定的
这个
的值不能在编译时确定。我想知道,一旦分配和构造了一个给定的对象,
this
的值是缓存的,还是每次使用时都会对表达式进行运行时评估?下面是激发我的问题的具体例子。被警告,这违反了每一个OOP原则和保护功能C++支持的目标。
int main()
{
    string s1 = string("I am super a super long string named s1, and won't be SSO");
    string s2 = string("I am super a super long string named s2, and won't be SSO");

    byte* s1interface = reinterpret_cast<byte*>(&s1);
    byte* s2interface = reinterpret_cast<byte*>(&s2);

    static_assert(sizeof s1 == sizeof s2);

    for(int offset(0); offset < sizeof s1; ++offset)
    {
        *(s1interface + offset) ^= *(s2interface + offset);
        *(s2interface + offset) ^= *(s1interface + offset);
        *(s1interface + offset) ^= *(s2interface + offset);
    }

    cout << s1 << '\n' << s2 << "\n\n\n";

    return 0;
}
//outputs:
//I am super a super long string **named s2**, and won't be SSO
//I am super a super long string **named s1**, and won't be SSO
//(The emphasis on the output strings was added by me to highlight the identity change)
intmain()
{
string s1=string(“我是一个名为s1的超长字符串,不会是SSO”);
string s2=string(“我是一个名为s2的超长字符串,不会是SSO”);
字节*s1接口=重新解释强制转换(&s1);
字节*s2interface=重新解释强制转换(&s2);
静态断言(sizeof s1==sizeof s2);
对于(整数偏移量(0);偏移量coutthis
的值从不被对象“查询”。它作为隐式参数传递给(非静态)对象方法

假设你有这个C++代码:

#include <stdio.h>

class mystring
{
public:
   char *data;  
   void print();
};

void mystring::print() 
{
   fputs(this->data, stdout);
}

void
main()
{
   mystring s = {"Hello World"};  

   s.print();
}

所以
这个
指针没有什么神奇之处。它和其他任何参数一样,只是一个无聊的参数。虚拟方法会让事情变得更有趣,但是
这个
的处理保持不变

,所以我联系了Stephan Lavavej(),负责维护Microsoft的标准库实现。我将在下面发布他的答案。我确实想指出,用户HAL9000的答案基本上是正确的,但由于Stephan的答案非常全面,我将发布它,并最终将其指定为正式答案(很难得到比实际执行标准的人的话更正式的答案)。如果你觉得这个答案信息丰富,HAL9000的答案有一个直观的例子来强化这个想法

斯蒂芬的话:

你不应该认为“this”指针存储在一个对象中。隐式参数心智模型是最准确的。 当函数x()调用Meow对象m上的成员函数Meow::y()时,x()必须已经知道m的地址。它可能是一个局部变量(x()知道其所有局部变量在堆栈上的位置),它可能是一个取消引用的指针(如果m是*ptr,ptr指向m),它可能通过引用传递(引用不是指针,但它们实际上具有与指针相同的位置信息),它可能是数组上的一个元素(如果m是arr[idx],那么arr+idx指向m),等等。因此Meow::y()将隐式传递m的地址,该地址在成员函数中变为“this”

至关重要的是,如果你有一个包含普通旧数据(例如一堆整数)的结构,并且你交换了两个结构的内容,那么对象不会改变身份,只会改变它们的内容。如果我把你家里的所有东西都拿出来,并与别人家里的东西交换,那么房子的位置就不会改变。(在C++中,对象不能从一个内存地址迁移到另一个内存地址——你最多可以做的是创建一个单独的对象,移动所有的东西,告诉任何关心旧位置的人,而不是指向新的位置,然后销毁原始对象的空壳——基本上是移动语义)。 因为“this”实际上并不存储在任何地方,所以它的成本为零,这一点很有用。(这是在虚拟成员函数之外,虚拟成员函数确实会让对象支付vptr成本,但这要高级得多。)

希望这有帮助, STL


存储指向其某个元素的指针的对象在执行此类操作后将不会处于正常状态。这就是为什么会发明赋值运算符。@PeteBecker我不担心对象,我担心编译器如何实现
这个
。对象是一个问题,每个问题都会被问到和回答这是一个指针,当您从中调用任何方法时,它作为隐藏参数传递class@schulmastercode
a.method(arg);
实际上是
Class:method(&a,arg);
,如果您理解这一点,那么您可以看到它既不存储也不查询,变量的地址是每次都“评估”,但只要二进制代码没有任何变量,它就只有地址——只要我记得正确,在某些版本的MSVC++32位中,
a.method(arg)
并不是严格等同于
Class::method(&a,arg)但“<代码> > <代码>指针通过了 ECX< /Cord>寄存器,所有参数都像平常一样在堆栈上传递。因此,Ilya Bursov的评论的经验法则高度依赖于C++调用约定,不一定是通用的。这是标准所规定的吗?如果没有,则有登记。不要相信这个理由,尽管这很有道理,也很常见。参见上面Daniel的评论……它必须作为隐藏参数传递,否则方法
print
如何知道它应该使用哪个对象?如果它要查询对象,它就已经需要一个指针来查询正确的对象了。这到底是怎么回事<作为一个参数,可能是实现依赖的。这是对通常所做的很好的描述,但是我相信@ SulMrdor是谨慎的。C++标准非常努力地避免它脱离实际的实现细节。而它可以被埋葬在别处,在[类]中。该标准没有提到这个
是如何进入该方法的。如果你能利用黑魔法或隐形传送,它可能和隐藏参数一样合法。在某种程度上,这就像堆栈、堆和v-tab一样
#include <stdio.h>

struct mystring
{
   char *data;  
};

void mystring_print(struct mystring *this) 
{
   fputs(this->data, stdout);
}

void
main()
{
   mystring s = {"Hello World"};  

   mystring_print(&s);
}