1. Trang chủ
  2. » Công Nghệ Thông Tin

HandBooks Professional Java-C-Scrip-SQL part 51 docx

6 170 0
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Định dạng
Số trang 6
Dung lượng 32,78 KB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

When that's the case, we need to tell clients of this class explicitly that copy construction and assignment are prohibited.. boost::noncopyable works by prohibiting access to its copy c

Trang 1

noncopyable

Header: "boost/utility.hpp"

The compiler is often a very good friend of the programmer, but not always One example of its friendliness is the way that it automatically provides copy

construction and assignment for our classes, should we decide not to do so

ourselves This can lead to some unpleasant surprises, if the class isn't meant to be copied (or assigned to) in the first place When that's the case, we need to tell

clients of this class explicitly that copy construction and assignment are prohibited I'm not talking about comments in the code, but about denying access to the copy constructor and copy assignment operator Fortunately, the compiler-generated copy constructor and copy assignment operator are not usable when the class has bases or data members that aren't copyable or assignable

boost::noncopyable works by prohibiting access to its copy constructor and assignment operator and then being used as a base class

Usage

To make use of boost::noncopyable, have the noncopyable classes derive privately from it Although public inheritance works, too, this is a bad practice Public inheritance says IS-A (denoting that the derived class also IS-A base) to people reading the class declaration, but stating that a class IS-A noncopyable seems a bit far fetched Include "boost/utility.hpp" when deriving from noncopyable

#include "boost/utility.hpp"

class please_dont_make_copies : boost::noncopyable {};

int main() {

please_dont_make_copies d1;

please_dont_make_copies d2(d1);

please_dont_make_copies d3;

d3=d1;

}

The preceding example does not compile The attempted copy construction of d2 fails because the copy constructor of noncopyable is private The attempted assignment of d1 to d3 fails because the copy assignment operator of

noncopyable is private The compiler should give you something similar to the following output:

Trang 2

noncopyable.hpp: In copy constructor

' please_dont_make_copies::please_dont_make_copies (const

please_dont_make_copies&)':

boost/noncopyable.hpp:27: error: '

boost::noncopyable::noncopyable(const boost::noncopyable&)' is

private

noncopyable.cpp:8: error: within this context

boost/noncopyable.hpp: In member function 'please_dont_make_copies&

please_dont_make_copies::operator=(const please_dont_make_copies&)':

boost/noncopyable.hpp:28: error: 'const boost::noncopyable&

boost::noncopyable::operator=(const boost::noncopyable&)' is private

noncopyable.cpp:10: error: within this context

We'll examine how this works in the following sections It's clear that copying an assignment is prohibited when deriving from noncopyable This can also be achieved by defining the copy constructor and copy assignment operator

privatelylet's see how to do that

Making Classes Noncopyable

Consider again the class please_dont_make_copies, which, for some reason, should never be copied

class please_dont_make_copies {

public:

void do_stuff() {

std::cout <<

"Dear client, would you please refrain from copying me?";

}

};

Because the compiler generates a copy constructor and an assignment operator, there's nothing about this class that prohibits copying or assignment

please_dont_make_copies p1;

please_dont_make_copies p2(p1);

please_dont_make_copies p3;

p3=p2;

Trang 3

We could fix this mess by declaring the copy constructor and copy assignment operator private or protected, and by adding a default constructor (which would no longer be generated by the compiler)

class please_dont_make_copies {

public:

please_dont_make_copies() {}

void do_stuff() {

std::cout <<

"Dear client, would you please refrain from copying me?";

}

private:

please_dont_make_copies(const please_dont_make_copies&);

please_dont_make_copies& operator=

(const please_dont_make_copies&);

};

That works very well, but it isn't as immediately apparent to

please_dont_make_copies' clients that it is noncopyable Seeing

noncopyable instead makes the class more obviously noncopyable with less typing

Using noncopyable

The class boost::noncopyable is intended to be used as a private base class, which effectively turns off copy construction and copy assignment operations Using the previous example, here's how the code would look when using

noncopyable:

#include "boost/utility.hpp"

class please_dont_make_copies : boost::noncopyable {

public:

void do_stuff() {

std::cout << "Dear client, you just cannot copy me!";

}

};

There's no need to declare the copy constructor or copy assignment operator Because we've derived from noncopyable, the compiler won't generate them either, which disables copying and copy assignment Terseness can lend clarity,

Trang 4

especially for such basic and distinct concepts such as this For a client reading the code, it is immediately apparent that this class cannot be copied, or copy assigned, because boost::noncopyable appears at the very start of the class definition One last note: Do you recall that the default access control for classes is private? That means that inheritance is private by default, too You could make this fact even more obvious by spelling it out like this:

class please_dont_make_copies : private boost::noncopyable {

It all depends on the audience; some programmers find such redundant information annoying and distracting, whereas others appreciate the clarification It's up to you

to decide which way is right for your classes, and your programmers Either way, using noncopyable is definitely better than "forgetting" the copy constructor and the copy assignment operator, and it's also clearer than privately declaring them

Remember the Big Three

As we have seen, noncopyable provides a convenient way of disabling copying and copy assignment for a class But when do we need to do that? Which are the circumstances that demand a user-defined copy constructor or copy assignment operator in the first place? There is a general answer to this question, one that just about always is correct: Whenever you need to define one of the destructor, the copy constructor, or the copy assignment operator, you also need to define the remaining two.[5] These three interoperate in important ways, and when one exists, the others typically must, too Let's assume that one of your classes has a member that is a pointer You have defined a destructor for proper deallocation, but you haven't bothered defining a copy constructor or a copy assignment operator This means that there are at least two potential defects in your code, which are easy to trigger

[5]

The name Law of the Big Three comes from C++ FAQs (see [2] in the

Bibliography for details)

class full_of_errors {

int* value_;

public:

full_of_errors() {

value_=new int(13);

}

Trang 5

~full_of_errors() {

delete value_;

}

};

Using this class, there are at least three ways of producing errors that aren't obvious

if one neglects to consider the copy constructor and the assignment operator that the compiler has graciously augmented the class with

full_of_errors f1;

full_of_errors f2(f1);

full_of_errors f3=f2;

full_of_errors f4;

f4=f3;

Note that the two equivalent ways of invoking the copy constructor here are on the second and third lines They both call the synthesized copy constructor, although the syntax is different The final error is on the last line, where the copy assignment operator makes sure that the same pointer is used and deleted by at least two

instances of full_of_errors Doing things correctly, we would have realized the need for copy assignment and copy construction right away, when we defined our destructor Here's what should have been done:

class not_full_of_errors {

int* value_;

public:

not_full_of_errors() {

value_=new int(13);

}

not_full_of_errors(const not_full_of_errors& other) :

value_(new int(*other.value_)) {}

not_full_of_errors& operator=

(const not_full_of_errors& other) {

*value_=*other.value_;

return *this;

}

~not_full_of_errors() {

delete value_;

}

};

Trang 6

So, whenever one of the big threecopy constructor, (virtual) destructor, and copy assignment operatoris manually defined in a class, think long and hard before deciding that the remaining two are unnecessary And, remember to use

boost::noncopyable if there is to be no copying at all!

Summary

There are many types for which we need to prohibit copying and copy assignment However, declaring the copy constructor and copy assignment operator private is often neglected for such types, and responsibility for knowing that copying doesn't make sense is transferred to clients of the type Even when types ensure that they cannot be copied or assigned, using private copy constructors and copy assignment operators, it isn't always clear to the client that this is the case Of course, the

compiler kindly informs those who try, but it may not be apparent where the error

is coming from Either way, the best we can do is to be explicit about it, and

deriving from noncopyable makes a clear statement It is immediately in view when scanning the declaration of the type When compiling, an error message almost certainly includes the name noncopyable And it also saves some typing, which is a killer argument for some

Use noncopyable when:

 Copying and copy assignment of types is not allowed

 Prohibition of copying and assignment should be as explicit as possible

Ngày đăng: 06/07/2014, 03:20