自觉阅读C++相关书籍也已不少。但是有些经典内容值得不断去重复回顾。随着项目开发经验的增加以及代码能力的增强,往常看起来很经典的知识,再一次回顾会产生很多优秀的想法。这是我阅读《C++必知必会》的初衷。这本书我个人认为涵盖C++面试的所有知识,因此,各位可以通过本文来快速了解掌握这本书的干货,对于面试部分更加游刃有余。
template<int n>
inline void average(int (&array)[n])
{
average_n(array,n);
}
void scanTo(const char** p, char c)
{
while(**p && **p!=c)
{
++*p;
}
}
(2)C++更安全的做法是使用指针引用作为函数形参。
void scanTo(const char*& p, char c)
{
while(*p && *p!=c)
{
++p;
}
}
Shape* sp = new Circle;
Circle* cp = static_cast<Circle*>(sp);
String::String(const char* init)
{
if(!init) init = "";
s_ = new char[strlen(init)+1];
strcpy(s_, init);
}
String& String::operator = (const char* str)
{
if(!str) str = "";
char* tmp = strcpy(new char[strlen(str)+1], str);
delete[] s_;
s_ = tmp;
return *this;
}
X(const X&);
X& operator=(const X&);
Handle& Handle::operator=(const Handle& that)
{
if(this!=&that)
{
//赋值
}
return *this;
}
void (*fp)(int);
(*fp)(12);
fp(12);
注意:非静态成员函数的地址不是一个指针,因此不可以将一个函数指针指向非静态成员函数。
int C::*pimC;
class Shape
{
Point center_;
};
class Circle:public Shape
{
double radius_;
};
Point Circle::*loc = & Shape::center_;
//Shape当中任何偏移量在Circle当中也是一个有效偏移量。
void (Shape::*mf1)(Point) = &Shape::moveTo;
int* f1(); //返回值为int*的函数
int (*f1)(); //一个指针,指向返回值为int的函数。
int (*afp2[N])(); //一个具有N个元素的数组,元素类型为指向“返回值为int”的函数指针。
typedef void (*FP)();
FP afp3[N]; //一个具有N个“类型为FP”的元素的数组,该类型与afp2相同
int *a1[N]; //一个具有N个int*元素的数组。
int (*ap1)[N]; //一个指针,指向一个具有N个int元素的数组。
int aFunc(double);
int (&rFunc)(double) = aFunc;
//通过虚函数来增加OOP的灵活性
class Func
{
public:
virtual ~Func();
virtual double operator()(double) = 0;
};
class NMFunc : public Func
{
public:
NMFunc(double (*f)(double)):f_(f){}
double operator()(double d){return f_(d);}
private:
double (*f_)(double);
};
//函数使用实体
double integrate(Func& f, double low, double high);
double aFunc(double x) {}
int main()
{
//如何利用多态性来灵活使用函数对象
NMFunc g(aFunc);
double area = integrate(g, 0.0, 2.7);
}
class Button
{
public:
Button(const string& label) : label_(label),action_(0){}
void setAction(void (*newAction)()){
action_ = newAction;
}
void onClick() const
{
if(action_) action_();
}
private:
string label_;
void (*action_)();
};
class Action
{
public:
virtual ~Action();
virtual void operator()()=0;
virtual Action* clone() const = 0; //原型模式
};
class PlayMusic : public Action
{
public:
PlayMusic(const tring& songFile):song_(song){}
void operator()();
private:
MP3 song_;
};
//Button可以和任何一个Action对象进行协作
class Button
{
public:
Button(const std::string& label):label_(label), action_(0){}
void setAction(const Action* newAction){
Action* temp = newAction->clone();
delete action_;
action_ = temp;
}
void onClick() const
{
if(action_) (*action_)();
}
private:
std::string label_;
Action* action_;
};
注意:将clone命令与Command模式相结合(Prototype),可以获得更大的灵活性
这部分存疑。
//接口类,Interface class,一个虚析构函数和一组虚函数指明Rollable对象能做什么。
class Rollable
{
public:
virtual ~Rollable();
virtual void roll()=0;
};
//要继承的类
class Shape
{
public:
virtual ~Shape();
virtual void draw() const = 0;
};
//圆形可以滚动
class Circle : public Shape, public Rollable
{};
//方形不能滚动
class Square : public Shape
{};
Shape* s = getSomeShape();
Rollable* roller = dynamic_cast<Rollable*>(s);
if(roller != nullptr)
roller->roll();
class Shape{};
class Subject{};
class ObservedBlob : public Shape, public Subject{};
ObservedBlob* ob = new ObservedBlob;
Shape* s = ob; //预定义转换
Subject* subj = ob; //预定义转换
ob == subj
//可以等价为下式:
//(delta为subject子对象在ObservedBlob对象中的偏移量。)
ob ? (ob + delta == subj) : (subj == 0)
void* v = subj;
if(ob == v)
//两者不相等,因为丢失了类型信息而不知道delta是什么。
class Meal
{
public:
virtual ~Meal();
virtual void eat() = 0;
virtual Meal* clone() const = 0;
};
class Spaghetti : public Meal
{
public:
Spaghetti(const Spaghetti&);
void eat();
Spaghetti * clone() const
{
return new Spaghetti(*this);
}
};
Meal* m = thatGuyMeal();
Meal* myMeal = m->clone();
class Shape
{
public:
virtual Shape* clone() const = 0;
};
class Circle : public Shape
{
public:
virtual Circle* clone() const;
};
//创建数组
const int numComs = 4;
SPort* comPorts = new (comAddr) SPort[numComs];
//销毁
int i = numComs;
while(i)
comPorts[--i].~SPort();
void *operator new(size_t) throw(bad_alloc);//operator new
void *operator new[](size_t) throw(bad_alloc);//array new
void operator delete(void*) throw();//operator delete
void operator delete[](void*)throw();//array delete
class Handle
{
public:
void* operator new(size_t);
void operator delete(void*);
void* operator new[](size_t n)
{
return ::operator new(n);
}
void operator delete[](void* p)
{
::operator delete(p);
}
};
arrT = new T[5];
//请求的内存量:5*sizeof(T) + delta
注意:编译器未必会给每一个数组分配都请求额外的内存空间,并且对于不同的数组分配而言,额外请求的内存空间大小也会发生变化。
void Button::setAction(const Action* newAction)
{
//先做可能会抛出异常的操作
Action* tmp = newAction->clone();
//再释放关键资源(析构不会抛出任何异常)
delete action_;
action_ = tmp;
}
class Resource{};
class ResourceHandle
{
public:
explicit ResourceHandle(Resource* aResource):r_(aResource){}//获取资源
~ResourceHandle(){
delete r_;
}
Resource* get(){
return r_;
}
private:
ResourceHandle(const ResourceHandle&);
ResourceHandle& operator=(const ResourceHandle&);
Resource* r_;
};
template<typename T>
class CheckedPtr
{
public:
explicit CheckedPtr(T* P):p_(P){}
~CheckedPtr(){delete p_;}
T* operator ->(){return get();}
T& operator *(){return *get();}
private:
T* p_;
//返回前检查指针为空
T* get(){
if(!p_)
{
throw NullCheckedPointer();
}
return p_;
}
CheckedPtr(const CheckedPtr&);
CheckedPtr& operator=(const CheckedPtr&);
};
(s.operator->())->draw();
template<typename T>//模板参数列表
class Heap; //模板名字
template<typename T>
class Heap<T*>;//模板id
template<typename T>//T是一个模板参数
class Heap{};
Heap<double> dHeap;//double是一个模板实参
template<typename T> class Heap;
template<>
class Heap<const char*>
template <typename T>
class Heap<T*>{};
(1)和类模板的完全特化不同,局部特化是一个模板,template关键字和参数列表是不可缺少的。
(2)与完全特化不同,这版本的Heap参数类型并没有完全确定,仅仅是部分地确定为T*。而T是一个未指定的类型。这就是为什么它是局部特化的原因。
//示例1
template<typename T, int n>
class Heap<T[n]>;
Heap<float*[6]> h8;
//示例2:局部特化,带有两个参数的非成员函数的指针对Heap进行特化
template<typename R, typename A1, typename A2>
class Heap<R (*)(A1,A2)>
template void Heap<double>::push(const double&);
//简单的非标准容器模板:以嵌套类型名字嵌入关于他们自己的信息
template <typename T>
class PtrList
{
public:
typedef T* ElemT;
void insert(ElemT);
};
//嵌套名字允许很容易地访问被PtrList模板认可的元素类型。
typedef PtrList<State> StateList;
StateList:ElemT currentState = 0;
//嵌套层次最深的名字E是一个类型名字。
typename A::B::C::D::E
总结:必须明确告诉编译器,某个嵌套的名字是一个类型名字。这样编译器才能正确解析。
template<typename T>
class SList
{
public:
template<typename In>
SList(In begin, In end);
};
//成员模板的实现
template<typename T> //针对一个SList
template<typename In> //针对一个成员
SList<T>::SList(In begin, In end):head_(0)
{
while(begin!=end)
{
push_front(*begin++);
}
reverse();
}
//T是double
//In是vector::iterator
SList<double> data2(rd2.begin(), rd2.end());
template <typename T>
class SList
{
public:
//这是为了防止S与T一样的情况下,编译器自己编写一个复制操作。
SList(const SList& that);
SList& operator=(const SList& rhs);
//成员复制函数,当S与T不是一个类型的时候。
template<typename S>
SList(const SList<S>& that);
template<typename S>
SList& operator=(const SList<S>& rhs);
};