与危险但实用的示例相关的C ++中“ this”关键字的实现限制

时间:2018-09-20 22:04:26

标签: c++ this

我知道给定this的值不能在编译时确定。我不禁要问,一旦分配并构造了给定的对象,是this的值被缓存,还是每次使用表达式时都会在运行时求值?这是激发我的问题的具体例子。请注意,这违反了C ++旨在维护的所有OOP原则和保护功能。

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)

首先,我想说这个程序不仅可以编译,而且可以始终如一地产生输出。我的问题不是基于为什么/如何运作。

按照我的看法,对象(重新)形成后,所有内部变量,甚至那些管理堆内存的内部变量都将被移植。但是,我设想了一个假设场景,其中this由对象查询然后在内部存储。在对象移植操作之后,&me将与在原始构造中被查询和存储的this不一致,这将严重破坏使用this的任何操作以及任何运行时地址反射。尽管绝对不要这样做,并且如果有人敢对自己的任何对象进行如此令人发指的事情,那么所有的赌注都将落空,《标准》是否规定对this进行持续评估,或者只是为了this进行评估在假设对象仅会占据放置对象的空间的情况下怎么说?

编辑:让我用另一种方式解释一下,如果在运行时该对象具有一个隐藏的内部this,则该对象一旦分配即被写入,并且随后对this的所有读取均读取存储的值,在移植之后&objectthis将不相同。显然,这不是我的编译器如何实现的,但是我想知道这是出于一致性还是运气。

2 个答案:

答案 0 :(得分:2)

对象永远不会“查询” this的值。它作为隐式参数传递给(非静态)对象方法。

假设您有以下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();
}

现在看来方法print没有任何参数,但实际上却有指向对象s的指针。因此,编译器将生成与此c程序等效的代码。

#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);
}

因此this指针没有什么神奇之处。和其他参数一样,它只是一个无聊的参数。使用虚拟方法会使事情变得更加有趣,但是this的处理保持不变

答案 1 :(得分:1)

因此,我与Stephan Lavavej(Stephan's website)接触,他一直在为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”。

     

至关重要的是,如果您的结构包含简单的旧数据(例如一堆int),并且交换了两个结构的内容,则对象不会更改身份-仅更改其内容。如果我将您所有的东西都拿到您家中,然后与别人家中的东西交换,则房屋的位置将保持不变。 (在C ++中,对象无法从一个内存地址迁移到另一个内存地址-您最多可以做的是创建一个单独的对象,移动所有内容,告诉关心旧位置的任何人改而指向新位置,然后销毁原始对象的空壳-基本上就是移动语义。)

     

因为“ this”实际上没有存储在任何地方,所以成本为零,这非常有用。 (这是外部虚拟成员函数,它确实使对象付出vptr成本,但这要先进得多。)

     

希望这会有所帮助,   STL