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

HandBooks Professional Java-C-Scrip-SQL part 55 pot

6 162 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

Tiêu đề Handbooks Professional Java-C-Scrip-SQL Part 55 Pot
Trường học University of Example
Chuyên ngành Computer Science
Thể loại Thesis
Năm xuất bản 2023
Thành phố Example City
Định dạng
Số trang 6
Dung lượng 22,55 KB

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

Nội dung

empty base optimization to make a base class that contains no data members, no virtual functions, and no duplicated base classes, to take zero space in derived class objects, and most mo

Trang 1

defines operator== It implements this operator in a generic fashion by using operator< for the parameterizing type Then, the class some_class, wishing

to utilize the services of equivalent, derives from it and passes itself as

equivalent's template parameter Therefore, the resulting operator== is defined for the type some_class, implemented in terms of some_class's operator< That's all there is to the Barton-Nackmann trick This is a simple yet immensely useful pattern, quite beautiful in its elegance

Strict Weak Ordering

I have already mentioned strict weak orderings twice in this book, and if you're not familiar with what they are, this brief digression should help A strict weak

ordering is a relation between two objects First, let's get a bit theoretical and then

we can make it more concrete For a function f(a,b) that implements a strict weak ordering, with a and b being two objects of the same type, we say that a and

b are equivalent if f(a,b) is false and f(b,a) is false This means that a does not precede b, and b does not precede a We can thus consider them to be

equivalent Furthermore, f(a,a) must always yield false[5] and if f(a,b) is true, then f(b,a) must be false.[6] Also, if f(a,b) and f(b,c) is true, then so is f(a,c).[7] Finally, if f(a,b) is false and f(b,a) is false, and if f(b,c) is false and f(c,b) is false, then f(a,c) is false and f(c,a)

is false.[8]

[5]

This is irreflexivity

[6]

This is antisymmetry

[7]

This is transitivity

[8]

This is transitivity of equivalence

Applying the preceding to our previous example (with the class thing) can help clarify the theory The less than comparison for things is implemented in terms

of less than for std::string This, in turn, is a lexicographical comparison So, given a thing a containing the string "First," a thing b containing the string

"Second," and a thing c containing the string "Third," let's assert the earlier definitions and axioms

#include <cassert>

Trang 2

#include <string>

#include "boost/operators.hpp"

// Definition of class thing omitted

int main() {

thing a("First");

thing b("Second");

thing c("Third");

// assert that a<b<c

assert(a<b && a<c && !(b<a) && b<c && !(c<a) && !(c<b));

// Equivalence

thing x=a;

assert(!(x<a) && !(a<x));

// Irreflexivity

assert(!(a<a));

// Antisymmetry

assert((a<b)==!(b<a));

// Transitivity

assert(a<b && b<c && a<c);

// Transitivity of equivalence

thing y=x;

assert( (!(x<a) && !(a<x)) &&

(!(y<x) && !(x<y)) &&

(!(y<a) && !(a<y)));

}

Now, all of these asserts hold, because std::string implements a strict weak ordering.[9] Just as operator< should define a strict weak ordering, so should operator> Later on, we'll look at a very concrete example of what happens when we fail to acknowledge the difference between equivalence (which

is required for a strict weak ordering) and equality (which is not)

[9]

In fact, std::string defines a total ordering, which is a strict weak ordering with the additional requirement that equivalence and equality are identical

Avoid Object Bloating

In the previous example, our class derived from two base classes:

less_than_comparable<thing> and equivalent<thing> Depending

on your compiler, you may pay a price for this multiple inheritance; thing may

be much larger than it needs to be The standard permits a compiler to use the

Trang 3

empty base optimization to make a base class that contains no data members, no virtual functions, and no duplicated base classes, to take zero space in derived class objects, and most modern compilers perform that optimization Unfortunately, using the Operators library often leads to inheriting from multiple classes and few compilers apply the empty base optimization in that case To avoid the potential object size bloating, Operators supports a technique known as base class chaining Every operator class accepts an optional, additional template parameter, from which it derives By having one concept class derive from another, which derives from another, which derives from another…(you get the idea), the multiple

inheritance is eliminated This alternative is easy to use Rather than inheriting from several base classes, simply chain the classes together, like so

// Before

boost::less_than_comparable<thing>,boost::equivalent<thing>

// After

boost::less_than_comparable<thing,boost::equivalent<thing> >

This method removes the inheritance from multiple empty base classes, which may not trigger your compiler's empty base optimization, in favor of derivation from a chain of empty base classes, increasing the chance of triggering the empty base optimization and reducing the size of the derived classes Experiment with your compiler to see what benefits you can gain from this technique Note that there is a limit to the length of the base class chain that depends upon the compiler There's also a limit to the length of the chain a human can grok! That means that classes that need to derive from many operator classes may need to group them Better yet, use the composite concepts already provided by the Operators library

The difference in size between using base class chaining and multiple inheritance

on a popular compiler[10] that doesn't perform the empty base class optimization for multiple inheritance is quite large for my tests Using base class chaining ensures that the size of types is not negatively affected, whereas with multiple inheritance, the size grows by 8 bytes for a trivial type (admittedly, 8 additional bytes isn't typically a problem for most applications) If the size of the wrapped type is very small, the overhead caused by multiple inheritance is potentially more than is

tolerable Because it is so easy, consider using base class chaining all the time!

[10]

I say this both because there's no need for calling names, and because everyone already knows that I'm talking about Microsoft's old compiler (their new one

rocks)

Trang 4

Operators and Different Types

Sometimes, an operator involves more than one type For example, consider a string class that supports concatenation from character arrays through

operator+ and operator+= The Operators library helps here too, by way of the two-argument versions of the operator templates In the case of the string class, there is probably a conversion constructor available that accepts a char*, but as

we shall see, that doesn't solve all of the problems for this class Here's the string class that we'll use

class simple_string {

public:

simple_string();

explicit simple_string(const char* s);

simple_string(const simple_string& s);

~simple_string();

simple_string& operator=(const simple_string& s);

simple_string& operator+=(const simple_string& s);

simple_string& operator+=(const char* s);

friend std::ostream&

operator<<(std::ostream& os,const simple_string& s);

};

As you can see, we've already added two versions of operator+= for

simple_string One accepts a const simple_string&, and the other accepts a const char* As is, our class supports usage like this

simple_string s1("Hello there");

simple_string s2(", do you like the concatenation support?");

s1+=s2;

s1+=" This works, too";

Although the preceding works as intended, we still haven't provided the binary operator+, an omission that the class' users definitely won't be pleased with Note that for our simple_string, we could have opted to enable concatenation

by omitting the explicit conversion constructor However, doing so would involve

an extra (unnecessary) copy of the character buffer, and the only savings would be the omission of an operator

// This won't compile

Trang 5

simple_string s3=s1+s2;

simple_string s4=s3+" Why does this class behave so strangely?";

Now let's use the Operators library to supply the missing operators for the class Note that there are actually three missing operators

simple_string operator+(const simple_string&,const simple_string&);

simple_string operator+(const simple_string& lhs, const char* rhs);

simple_string operator+(const char* lhs, const simple_string& rhs);

When defining operators manually, it's easy to forget one of the overloads for taking one const simple_string& and one const char* When using the Operators library, you can't forget, because the library is implementing the missing operators for you! What we want for simple_string is the addable concept, so we simply derive simple_string from

boost::addable<simple_string>

class simple_string : boost::addable<simple_string> {

In this case, however, we also want the operators that allow mixing

simple_strings and const char*s To do this, we must specify two

typesthe result type, simple_string, and the second argument type, const char* We'll utilize base class chaining to avoid increasing the size of the class

class simple_string :

boost::addable<simple_string,

boost::addable2<simple_string,const char*> > {

This is all that's needed for supporting the full set of operators that we aimed for!

As you can see, we used a different operator class: addable2 If you're using a compiler that supports partial template specialization, you don't have to qualify the name; use addable instead of addable2 There are also versions of the classes with the suffix "1" provided for symmetry It may increase the readability to

always be explicit about the number of arguments, which gives us the following derivation for simple_string

class simple_string :

boost::addable1<simple_string,

boost::addable2<simple_string,const char*> > {

Trang 6

Choose between them according to taste, and if your compiler supports partial template specialization, the simplest choice is to omit the suffixes altogether class simple_string :

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