Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Professional C++ [eng].pdf
Скачиваний:
284
Добавлен:
16.08.2013
Размер:
11.09 Mб
Скачать

Overloading C++ Operators

delete mem;

mem = new MemoryDemo[10]; delete [] mem;

mem = new (nothrow) MemoryDemo(); delete mem;

mem = new (nothrow) MemoryDemo[10]; delete [] mem;

return (0);

}

Here is the output from running the program:

operator new operator delete operator new[] operator delete[]

operator new nothrow operator delete operator new[] nothrow operator delete[]

These implementations of operator new and operator delete are obviously trivial and not particularly useful. They are intended only to give you an idea of the syntax in case you ever want to implement nontrivial versions of them.

Whenever you overload operator new, overload the corresponding form of operator delete. Otherwise, memory will be allocated as you specify but freed according to the built-in semantics, which may not be compatible.

It might seem overkill to overload all of the various forms of operator new. However, it’s generally a good idea to do so in order to prevent inconsistencies in the memory allocations. If you don’t want to provide implementations, you can declare the function as protected or private in order to prevent anyone from using it.

Overload all forms of operator new, or provide private declarations without implementations to prevent their use.

Overloading operator new and operator delete with Extra Parameters

In addition to overloading the standard forms of operator new, you can write your own versions with extra parameters. For example, here is the MemoryDemo class showing an additional operator new with an extra integer parameter:

461

Chapter 16

#include <new> using namespace std;

class MemoryDemo

{

public:

MemoryDemo();

~MemoryDemo();

void* operator new(size_t size) throw(bad_alloc); void operator delete(void* ptr) throw();

void* operator new[](size_t size) throw(bad_alloc); void operator delete[](void* ptr) throw();

void* operator new(size_t size, const nothrow_t&) throw(); void operator delete(void* ptr, const nothrow_t&) throw(); void* operator new[](size_t size, const nothrow_t&) throw(); void operator delete[](void* ptr, const nothrow_t&) throw(); void* operator new(size_t size, int extra) throw(bad_alloc);

};

void* MemoryDemo::operator new(size_t size, int extra) throw(bad_alloc)

{

cout << “operator new with extra int arg\n”; return (::operator new(size));

}

When you write an overloaded operator new with extra parameters, the compiler will automatically allow the corresponding new expression. So, you can now write code like this:

int x = 5;

MemoryDemo* memp = new(5) MemoryDemo(); delete memp;

The extra arguments to new are passed with function call syntax (as in nothrow new). These extra arguments can be useful for passing various flags or counters to your memory allocation routines.

You cannot add arbitrary extra arguments to operator delete. However, an alternate form of operator delete gives you the size of the memory that should be freed as well as the pointer. Simply declare the prototype for operator delete with an extra size parameter.

If your class declares two identical versions of operator delete except that one takes the size parameter and the other doesn’t, the version without the size parameter will always get called. If you want the version with the size to be used, write only that version.

You can replace operator delete with the version that takes a size for any of the versions of operator delete independently. Here is the MemoryDemo class definition with the first operator delete modified to take the size of the memory to be deleted.

#include <new> using namespace std;

462

Overloading C++ Operators

class MemoryDemo

{

public:

MemoryDemo();

~MemoryDemo();

void* operator new(size_t size) throw(bad_alloc); void operator delete(void* ptr, size_t size) throw(); void* operator new[](size_t size) throw(bad_alloc); void operator delete[](void* ptr) throw();

void* operator new(size_t size, const nothrow_t&) throw(); void operator delete(void*ptr, const nothrow_t&) throw(); void* operator new[](size_t size, const nothrow_t&) throw(); void operator delete[](void* ptr, const nothrow_t&) throw(); void* operator new(size_t size, int extra) throw(bad_alloc);

};

The implementation of this operator delete calls the global operator delete without the size parameter because there is no global operator delete that takes the size.

void MemoryDemo::operator delete(void* ptr, size_t size) throw()

{

cout << “operator delete with size\n”; ::operator delete(ptr);

}

This capability is useful only if you are writing a complicated memory allocation and deallocation scheme for your classes.

Summar y

This chapter summarized the rationale for operator overloading, and provided examples and explanations for overloading the various categories of operators. We hope that this chapter taught you to look past the ugly syntax of operator overloading, and to appreciate the power that it gives you.

Subsequent chapters in this book employ operator overloading to provide abstractions, including iterators in Chapter 23 and smart pointers in Chapter 25.

463

Writing Efficient C++

The efficiency of your programs is important regardless of your application domain. If your product competes with others in the marketplace, speed can be a major differentiator: given the choice between a slower and a faster program, which one would you choose? No one would buy an operating system that takes two weeks to boot up, unless it was the only option. Even if you don’t intend to sell your products, they will have users. Those users will not be happy with you if they end up wasting time waiting for your programs to complete tasks.

Now that you understand the concepts of Professional C++ design and coding, and have tackled some of the more complex facilities that the language provides, you are ready to incorporate performance into your programs. Writing efficient programs involves thought at the design level, as well as details at the implementation level. Although this chapter falls late in this book, remember to consider performance from the beginning of your program life cycle.

This chapter first provides working definitions of “efficiency” and “performance” as they relate to software, describes the two levels at which you can increase efficiency in your programs, and discusses the two major classes of applications. Specific strategies follow for writing efficient programs, including language-level optimizations and design-level guidelines. Finally, the chapter provides an in-depth discussion of profiling tools.

Over view of Performance and Efficiency

Before delving further into the details, it’s helpful to define the terms performance and efficiency, as used in this book. The performance of a program can refer to several areas, such as speed, memory usage, disk access, and network use. This chapter focuses on speed performance. The term efficiency, when applied to programs, means running without wasted effort. An efficient program completes its tasks as quickly as possible within the given circumstances. A program can be efficient without being fast, if the application domain is inherently prohibitive to quick execution.