为驳回编译器自动提供的功能,可将相应的成员函数声明为private并且不予实现,更好的做法如上图。
class Investment{ ... };
Investment* createInvestment(); //工厂函数,返回动态分配对象
void f()
{
Inerstment* pInv = createInvestment();
...
delete pInv;
}
class Window{
public:
...
std::string name() const;
virtual void display() const;
};
class WindowWithScrollBars: public Window {
public:
...
virtual void display() const;
};
void printNameAndDisplay(Window w) //不正确吗,参数可能被切割
{
std::cout << w.name();
w.display();
}
void printNameAndDisplay(const Window& w) //很好,参数不会被切割
{
std::cout << w.name();
w.display();
}
WindowWithScrollBars wwsb;
printNameAndDisplay(wwsb);
class Date; //class 声明式
Date today(); //by-val返回
void clearAppointments(Date d); //需要传入Date形参
#include
using namespace std;
class Base{
public:
void healthValue() {
dohealthValue();
}
private:
virtual void dohealthValue()const{
cout << "Base~~~healthValue\n";
}
};
class Derive: public Base{
private:
virtual void dohealthValue()const{
cout << "Derive~~~healthValue\n";
}
};
int main()
{
Base *b = new Derive();
b->healthValue();
return 0;
}
1.面向对象编程世界总是以显式接口和运行期多态解决问题。
class Widget{
public:
widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
...
};
void doProessing(Widget& w)
{
if(w.size() > 10 && w!= someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
2.由于w的类型被声明为Widget,所以w必须支持Widget接口。我们可以在源码中找到这个接口(例如在Widget的.h文件中),看看它是什么样子,所以我们称此接口为一个显式接口,也就是它在源码中明确可见。
3. 由于Widget的某些成员函数是virtual,w对那些函数的调用将表现出运行期多态(runtime polymorphism),也就是说将于运行期根据w的动态类型决定究竟是哪一个函数。
4. Templates及泛型编程的世界,与面向对象有根本的不同。在此世界中接口和运行期多态仍然存在,但重要性降低,反倒是隐式接口(implicit interfaces)和编译器多态(compile-time polymorphism)移到前头了。若想知道那是什么,看看当我们将doProcessing从函数转变成函数模板时发生了什么事:
template<typename T>
void doProcessing(T& w)
{
if(w.size() > 10 && w!= someNastyWidget) {
Widget temp(w);
temp.normalize();
temp.swap(w);
}
}
w必须支持哪一种接口,系由template中执行与w身上的操作来决定。本例看来w的类型T好型必须支持size,normalize和swap成员函数、copy成员函数、copy构造函数(用来建立temp)、不等比较(inequality comparison,用来比较someNasty-Widget)。我们很快会喊道这并非完全正确,但对目前而言足够真实。重要的是,这一组表达式(对此template而言必须有效编译)便是T必须支持的一组隐式接口(implicit interface)。
凡涉及w的任何函数调用,例如operator>和operator!=,有可能造成template具现化,使这些调用得以成功。这样的具现行为发生在编译期。“以不同的tempalte参数具现化function template”会导致调用不同的函数,这便是所谓的编译期多态(complie-time polymorphism)。
5. "运行期多态"和“编译期多态”之间的差异,因为它类似于“哪一个重载函数该被调用”和”哪一个virtual函数该被绑定“(发生在运行期)之间的差异。
6. 显式接口和隐式接口的差异比较新颖,通常显式接口由函数的签名式(也就是函数名称、参数类型、返回类型)构成。
class Widget{
public:
Widget();
virtual ~Widget();
virtual std::size_t size() const;
virtual void normalize();
void swap(Widget& other);
};
其public接口由一个构造函数、一个析构函数、函数size,normalize,swap及其参数类型、返回类型、常量性构成。也包括编译器产生的copy构造函数和copy assignment操作符。另外也可以包括typedefs,以及public成员变量。
隐式接口完全不同,它并不基于函数签名式,二十由有效表达式组成。再次看看doProcessing template一开始的条件:
template<typename T>
void doProcessing(T& w)
{
if(w.size() > 10 && w!= someNastyWidget) {
...
}
}
T(w的类型)的隐式接口看来好像有这些约束:
tempalte<typename C> //允许使用"typename"或"class"
void f(const C& container, //不允许使用"typename"
typename C::interator iter);//一定要使用"typename"
template<typename T>
class Derived: public Base<T>::Nested { //base class list中,不允许使用"typename"
public:
explicit Derived(int x) : Base<T>::Nested(x) //mem,init.list中,不允许使用"typename"
{
typename Base<T>::Nested temp; //嵌套从属类型名称,不在上述两例外情况中,需要加上"typename"
...
}
...
};
1.当我们编写一个class template,而它所提供的之与此template相关的“”函数支持“所有参数之隐式类型转换”时,请将那些函数定义为“class template的friend函数”。
/*******************NewHandlerHolder*****************/
class NewHandlerHolder{
public:
explicit NewHandlerHolder(std::new_handler nh): handler(nh){}
~NewHandlerHolder(){std::set_new_handler(handler);}
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder&);
NewHandlerHolder& operator=(const NewHandlerHolder&);
};
/*******************Widget*****************/
class Widget{
public:
static std::new_handler set_new_handler(std::new_handler p) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
std::new_handler Widget::currentHandler = 0;
std::new_handler Widget::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
/*******************override operator new*****************/
void * Widget::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size);
}
/*******************using*****************/
void outOfMem()
{
cerr << "Unable to satisfy request for memory\n";
abort();
}
int main()
{
Widget::set_new_handler(outOfMem);
Widget* pw1 = new Widget;
std::string *ps = new std::string;
Widget::set_new_handler(0);
Widget* pw2 = new Widget;
}
/*******************NewHandlerHolder*****************/
class NewHandlerHolder{
public:
explicit NewHandlerHolder(std::new_handler nh): handler(nh){}
~NewHandlerHolder(){std::set_new_handler(handler);}
private:
std::new_handler handler;
NewHandlerHolder(const NewHandlerHolder&);
NewHandlerHolder& operator=(const NewHandlerHolder&);
};
/*******************template NewHandlerSupport*****************/
template<typename T>
class NewHandlerSupport{
public:
static std::new_handler set_new_handler(std::new_handler P) throw();
static void* operator new(std::size_t size) throw(std::bad_alloc);
private:
static std::new_handler currentHandler;
};
template<typename T>
std::new_handler NewHandlerSupport<T>::set_new_handler(std::new_handler p) throw()
{
std::new_handler oldHandler = currentHandler;
currentHandler = p;
return oldHandler;
}
template<typename T>
void* NewHandlerSupport<T>::operator new(std::size_t size) throw(std::bad_alloc)
{
NewHandlerHolder h(std::set_new_handler(currentHandler));
return ::operator new(size);
}
template<typename T>
std::new_handler NewHandlerSupport<T>::currentHandler = 0;
/*******************class Widget*****************/
class Widget: public NewHandlerSupport<Widget> {
};
/*******************using*****************/
void outOfMem()
{
cerr << "Unable to satisfy request for memory\n";
abort();
}
int main()
{
Widget::set_new_handler(outOfMem);
Widget* pw1 = new Widget;
std::string *ps = new std::string;
Widget::set_new_handler(0);
Widget* pw2 = new Widget;
}
替换编译器提供的operator new或operator delete三个最常见的理由。
①用来检测运用上的错误。
②为了强化效能。
③为了收集使用上的统计数据。
了解何时可在“全局性的”或“class专属的”基础上合理替换缺省的new和delete。
①为了检测运用错误(如前所述)。
②为了收集动态分配内存只使用统计信息(如前所述)。
③为了增加分配和归还速度。
④为了降低缺省内存管理器带来的空间额外开销。
⑤为了弥补缺省分配器中的非最佳齐位。
⑥为了将相关对象成簇集中。
⑦为了获得非传统的行为。