Skip to main content

Check for assignment to self in operator=.

Item 17: Check for assignment to self in operator=.
An assignment to self occurs when you do something like this:
class X { ... };
X a;
a = a;
// a is assigned to itself
This looks like a silly thing to do, but it's perfectly legal, so don't doubt for a moment that programmers do it.
More importantly, assignment to self can appear in this more benign-looking form:
a = b;
If b is another name for a (for example, a reference that has been initialized to a), then this is also an assignment
to self, though it doesn't outwardly look like it. This is an example of aliasing: having two or more names for the
same underlying object. As you'll see at the end of this Item, aliasing can crop up in any number of nefarious
disguises, so you need to take it into account any time you write a function.
Two good reasons exist for taking special care to cope with possible aliasing in assignment operator(s). The
lesser of them is efficiency. If you can detect an assignment to self at the top of your assignment operator(s), you
can return right away, possibly saving a lot of work that you'd otherwise have to go through to implement
assignment. For example, Item 16 points out that a proper assignment operator in a derived class must call an
assignment operator for each of its base classes, and those classes might themselves be derived classes, so
skipping the body of an assignment operator in a derived class might save a large number of other function
calls.
A more important reason for checking for assignment to self is to ensure correctness. Remember that an
assignment operator must typically free the resources allocated to an object (i.e., get rid of its old value) before
it can allocate the new resources corresponding to its new value. When assigning to self, this freeing of
resources can be disastrous, because the old resources might be needed during the process of allocating the new
ones.
Consider assignment of String objects, where the assignment operator fails to check for assignment to self:
class String {
public:
String(const char *value);
~String();
// see Item 11 for
// function definition
// see Item 11 for
// function definition
...
String& operator=(const String& rhs);
private:
char *data;
};
// an assignment operator that omits a check
// for assignment to self
String& String::operator=(const String& rhs)
{
delete [] data;
// delete old memory
// allocate new memory and copy rhs's value into it
data = new char[strlen(rhs.data) + 1];
strcpy(data, rhs.data);
return *this;
// see Item 15
}
Consider now what happens in this case:
String a = "Hello";
a = a;
// same as a.operator=(a)
Inside the assignment operator, *this and rhs seem to be different objects, but in this case they happen to be
different names for the same object. You can envision it like this:
The first thing the assignment operator does is use delete on data, and the result is the following state of affairs:
Now when the assignment operator tries to do a strlen on rhs.data, the results are undefined. This is because
rhs.data was deleted when data was deleted, which happened because data, this->data, and rhs.data are all the
same pointer! From this point on, things can only get worse.
By now you know that the solution to the dilemma is to check for an assignment to self and to return immediately
if such an assignment is detected. Unfortunately, it's easier to talk about such a check than it is to write it,
because you are immediately forced to figure out what it means for two objects to be "the same."
The topic you confront is technically known as that of object identity, and it's a well-known topic in
object-oriented circles. This book is no place for a discourse on object identity, but it is worthwhile to mention
the two basic approaches to the problem.
One approach is to say that two objects are the same (have the same identity) if they have the same value. For
example, two String objects would be the same if they represented the same sequence of characters:
String a = "Hello";
String b = "World";
String c = "Hello";
Here a and c have the same value, so they are considered identical; b is different from both of them. If you
wanted to use this definition of identity in your String class, your assignment operator might look like this:
String& String::operator=(const String& rhs)
{
if (strcmp(data, rhs.data) == 0) return *this;
...
}
Value equality is usually determined by operator==, so the general form for an assignment operator for a class
C that uses value equality for object identity is this:
C& C::operator=(const C& rhs)
{
// check for assignment to self
if (*this == rhs)
// assumes op== exists
return *this;
...
}
Note that this function is comparing objects (via operator==), not pointers. Using value equality to determine
identity, it doesn't matter whether two objects occupy the same memory; all that matters is the values they
represent.
The other possibility is to equate an object's identity with its address in memory. Using this definition of object
equality, two objects are the same if and only if they have the same address. This definition is more common in
C++ programs, probably because it's easy to implement and the computation is fast, neither of which is always
true when object identity is based on values. Using address equality, a general assignment operator looks like
this:
C& C::operator=(const C& rhs)
{
// check for assignment to self
if (this == &rhs) return *this;
...
}
This suffices for a great many programs.
If you need a more sophisticated mechanism for determining whether two objects are the same, you'll have to
implement it yourself. The most common approach is based on a member function that returns some kind of
object identifier:
class C {
public:
ObjectID identity() const;
// see also Item 36
...
};
Given object pointers a and b, then, the objects they point to are identical if and only if a->identity() ==
b->identity(). Of course, you are responsible for writing operator== for ObjectIDs.
The problems of aliasing and object identity are hardly confined to operator=. That's just a function in which you
are particularly likely to run into them. In the presence of references and pointers, any two names for objects of
compatible types may in fact refer to the same object. Here are some other situations in which aliasing can show
its Medusa-like visage:
class Base {
void mf1(Base& rb);
// rb and *this could be
// the same
...
};
void f1(Base& rb1,Base& rb2);
class Derived: public Base {
// rb1 and rb2 could be
// the same
void mf2(Base& rb);
// rb and *this could be
// the same
...
};
int f2(Derived& rd, Base& rb);
// rd and rb could be
// the same
These examples happen to use references, but pointers would serve just as well.
As you can see, aliasing can crop up in a variety of guises, so you can't just forget about it and hope you'll never
run into it. Well, maybe you can, but most of us can't. At the expense of mixing my metaphors, this is a clear case
in which an ounce of prevention is worth its weight in gold. Anytime you write a function in which aliasing
could conceivably be present, you must take that possibility into account when you write the code.

Comments

Popular posts from this blog

OWASP Top 10 Threats and Mitigations Exam - Single Select

Last updated 4 Aug 11 Course Title: OWASP Top 10 Threats and Mitigation Exam Questions - Single Select 1) Which of the following consequences is most likely to occur due to an injection attack? Spoofing Cross-site request forgery Denial of service   Correct Insecure direct object references 2) Your application is created using a language that does not support a clear distinction between code and data. Which vulnerability is most likely to occur in your application? Injection   Correct Insecure direct object references Failure to restrict URL access Insufficient transport layer protection 3) Which of the following scenarios is most likely to cause an injection attack? Unvalidated input is embedded in an instruction stream.   Correct Unvalidated input can be distinguished from valid instructions. A Web application does not validate a client’s access to a resource. A Web action performs an operation on behalf of the user without checking a shared sec

CKA Simulator Kubernetes 1.22

  https://killer.sh Pre Setup Once you've gained access to your terminal it might be wise to spend ~1 minute to setup your environment. You could set these: alias k = kubectl                         # will already be pre-configured export do = "--dry-run=client -o yaml"     # k get pod x $do export now = "--force --grace-period 0"   # k delete pod x $now Vim To make vim use 2 spaces for a tab edit ~/.vimrc to contain: set tabstop=2 set expandtab set shiftwidth=2 More setup suggestions are in the tips section .     Question 1 | Contexts Task weight: 1%   You have access to multiple clusters from your main terminal through kubectl contexts. Write all those context names into /opt/course/1/contexts . Next write a command to display the current context into /opt/course/1/context_default_kubectl.sh , the command should use kubectl . Finally write a second command doing the same thing into /opt/course/1/context_default_no_kubectl.sh , but without the use of k

标 题: 关于Daniel Guo 律师

发信人: q123452017 (水天一色), 信区: I140 标  题: 关于Daniel Guo 律师 关键字: Daniel Guo 发信站: BBS 未名空间站 (Thu Apr 26 02:11:35 2018, 美东) 这些是lz根据亲身经历在 Immigration版上发的帖以及一些关于Daniel Guo 律师的回 帖,希望大家不要被一些马甲帖广告帖所骗,慎重考虑选择律师。 WG 和Guo两家律师对比 1. fully refund的合约上的区别 wegreened家是case不过只要第二次没有file就可以fully refund。郭家是要两次case 没过才给refund,而且只要第二次pl draft好律师就可以不退任何律师费。 2. 回信速度 wegreened家一般24小时内回信。郭律师是在可以快速回复的时候才回复很快,对于需 要时间回复或者是不愿意给出确切答复的时候就回复的比较慢。 比如:lz问过郭律师他们律所在nsc区域最近eb1a的通过率,大家也知道nsc现在杀手如 云,但是郭律师过了两天只回复说让秘书update最近的case然后去网页上查,但是上面 并没有写明tsc还是nsc。 lz还问过郭律师关于准备ps (他要求的文件)的一些问题,模版上有的东西不是很清 楚,但是他一般就是把模版上的东西再copy一遍发过来。 3. 材料区别 (推荐信) 因为我只收到郭律师写的推荐信,所以可以比下两家推荐信 wegreened家推荐信写的比较长,而且每封推荐信会用不同的语气和风格,会包含lz写 的research summary里面的某个方面 郭家四封推荐信都是一个格式,一种语气,连地址,信的称呼都是一样的,怎么看四封 推荐信都是同一个人写出来的。套路基本都是第一段目的,第二段介绍推荐人,第三段 某篇或几篇文章的abstract,最后结论 4. 前期材料准备 wegreened家要按照他们的模版准备一个十几页的research summary。 郭律师在签约之前说的是只需要准备五页左右的summary,但是在lz签完约收到推荐信 ,郭律师又发来一个很长的ps要lz自己填,而且和pl的格式基本差不多。 总结下来,申请自己上心最重要。但是如果选律师,lz更倾向于wegreened,