《effective c++》
1.模数数值比较:
普通写法:
if(3 == A.Length())
{
do sth;
}
专业且更好维护写法:
const int B = 3;
if(B == A.Length())
{
do sth;
}
改‘3'为别的常量,只用改一个地方
--------------------------------------
2.__cplusplus 宏定义 查看编译器支持的C++版本
std::cout << __cplusplus <<std::endl;
--------------------
3.善用auto语法糖
--------------------
4.ranged-base for:
//容器遍历
for(decl : coll){
Do Sth.
}
for(int i : {1,3,5,6,7}){
std::cout << i << std:: endl;
}
Vector<MyPoint> Ve_Point;
for(auto& i : Ve_Point){
Do Sth.
}
--------------------
5.虚函数动态绑定应用例子:
绘图父类A,子类B/C/D/E继承,
A* object = new B/C/D/E();
虚函数virtual draw,List<A*> Mylist;
Mylist遍历时调用的是每种图形子类自己的draw;
动态绑定必要条件:虚函数,向上转型,指针调用
--------------------
6.带有虚函数的类,对象内存所占大小会自动加一个指针大小。
编译器会为每个有虚函数的类创建一个虚函数表(vtbl),该虚函数表将被该类的所有对象共享
class A
{
public:
virtual vfun1();
private:
int idata1,idata2;
}
sizeof(class A) == 2*sizeof(int) + sizeof(指针) //不考虑内存对齐情况下,实际不一定
---------------------
7.重载函数-编译器二义性报错
explicit parameterwidget(QWidget *parent = nullptr,\
double* pdAngleDivision = nullptr,\
double* pdOverAngle = nullptr,\
double* pdOverLength = nullptr,\
int* piRepetition = nullptr);
explicit parameterwidget(QWidget *parent = nullptr,\
double* pdStartX = nullptr,\
double* pdStartY = nullptr);
编译器不会根据参数数量匹配,参数类型雷同情况可更改参数顺序;(比如把上例中的INT*挪到第二个)
模板类
calss template
//模板类头文件
//C++模板中 typename关键字 ≈ class关键字;有时必要使用typename
//复数类:接受各类type的输入******************
temppalte<typename T>
calss complex
{
public:
complex(T r = 0 ,T i=0)
:re (r).im(i)
{}
comlpex& operator += (const complex&);
//内联函数(inline在头文件内定义)-->处理快,但是否真的inline由编译器决定
T real() const {return re;}
T imag() const {return im;}
//OOP概念:数据要独立
private:
T re , im;
friend complex& __doapl(complex*,const complex);
};
complex<double> c1(2.5,1.5);
complex<int> c2(2,6);
传址为什么比传值好,传地址只要4个字节效率高。
相同class的各个object互为友元。
多层封装(如果多个函数用一个底层功能),提高复用。
操作符重载-非成员函数(全局重载)
complex operator + (const complex& x,const complex&y)
{
return complex(real(x) + real(y),imag(x)+imag(y));
}
complex c1(2,1);
complex c2;
c2 = c1 + c2;
依靠参数,编译器可区分操作符重载
complex operator + (const complex& x)
{
return x;
}
complex operator - (const complex& x)
{
return complex(-real(x),-imag(x));
}
cout << +c1;
一个复数类完整设计思路
头文件
#ifndef __COMPLEX__
#define __COMPLEX__
class complex
{
public:
// 构造函数
// 初始化列表在构造函数执行前执行
// :re(r),im(i) == {this->re = r; this->im = i}
*******************************************************
complex(double r = 0,i= 0)
:re(r),im(i) {}
// 复数的实部和虚部
// 内联函数
*******************************************************
double real() const() {return re;}
double imag() const() {return im;}
// 重载 += 操作符
*******************************************************
complex& operator += (const complex&);
private:
double re , im;
friend complex& __doapl(complex*,const complex&);
}
#endif
.cpp:
//请求编译器内联
inline complex& __doapl(complex* ths,const complex& r)
{
//友元函数允许访问类的私有成员: r.re、r.im
ths->re += r.re;
ths->im += r.im;
return *ths;
}
//非成员函数重载,方便其它复用
//复数 + 复数
//返回值传值,需要的是重新构造的对象的值。如果不用重新构造,可考虑传址
inline complex opreator + (const complex& x,const complex& y)
{
return complex(x.real() + y.real(),x.imag() + y.imag());
}
//复数 + 实数...
//实数 + 复数...
//请求编译器内联-成员函数
inline complex::operator += (const complex& r)
{
return __doapl(this,r);
}
BigThree
C++编译器会自动创建
析构函数(Destructor)~
复制构造函数(copy constructor) People P2(P1)
复制赋值运算符(copy assignment operator)People P2 = People P1
三法则(英语:The Big Three;三法则,三大定律)在 C++ 程序设计里,它是一个以设计的基本原则而制定的定律。
三法则的要求在于,假如类有明显地定义下列其中一个成员函数,那么程序员必须连其他二个成员函数也一同编写至类内,亦即下列三个成员函数缺一不可。
析构函数(Destructor)
复制构造函数(copy constructor)
复制赋值运算符(copy assignment operator)
上述三个函数是特别的成员函数,假如程序员没有自行定义或是编写声明它们,那么编译器会自动地创建它们,并且会编译至应用程序内。
编译器实现new操作过程
Complex* A = new Complex(2,1);
Operator new == malloc(n);
1. void *mem = operator new (sizeof(Complex));//申请地址
2. A = static_cast<Complex*>(mem);//类型转换
3. A->Complex::Complex(2,1);//构造函数
编译器实现delete操作过程
delete A;
Operator delete() == free()
A->Complex::~Complex();//析构函数
Operator delete(A);//
数组\字符串 要用delete[] 保证删除所有分配的内存内容
组合(Compositions)
Container容器类使用到了component组件类
构造函数顺序:首先调用component的默认(defalut)构造函数,再自己的构造函数.由内而外
析构函数顺序:先调用自己的(Container)析构函数,再调用component的析构函数.由外而内
委托(Delegation)
本质为Compositions by reference
"Handle\body"设计模式,编译防火墙(客户端不用重复编译,实现端更改)(待补全)
"Copy On write"概念,变动时复制一份变动(待补全)
继承(Inheritance)
父类的析构函数要设为Virtual
non-virtual函数:不希望被子类重新定义.
virtual函数:已有默认定义,也可以被子类重构.
pure virtual函数:父类本身无定义,一定要子类重写.
设计模式:Template Method
抽象类(AbstractClass): 定义一组基本方法供子类实现,定义并实现组合了基本方法的模板方法。
具体子类 (ConcreteClass): 实现原语操作以完成算法中与特定子类相关的步骤。
类的转换函数(conversion function)
//分数类,只要类的转换合理,可以写多个转换函数。operator type() const{xxx}
class Fraction
{
public:
Fraction(int num,int den = 1)
:m_numerator(num),m_denominator(den)
operator double() const{
return (double) (m_numerator/m_denominator);}
private:
int m_numerator;
int m_denominator;
}
Fraction f(3,5);
double a = 4 + f;
执行到 “double a = 4 + f;”时,编译器想要编译通过,
编译器会去找是否有“+”的重载;
没有,则找转换函数,编译器发现可以编译通过。
隐式转换(non-explicit-one-argument ctor)
explicit关键字只对有一个参数的类构造函数有效, 如果类构造函数参数大于或等于两个时, 是不会产生隐式转换的。
但是, 也有一个例外, 就是当除了第一个参数以外的其他参数都有默认值的时候, explicit关键字依然有效, 此时, 当调用构造函数时只传入一个参数, 等效于只有一个参数的类构造函数。
class Fraction
{
public:
Fraction(int num,int den = 1)
:m_numerator(num),m_denominator(den)
Ⅰ:
operator double() const{
return (double) (m_numerator/m_denominator);}
Ⅱ://+后只允许Fraction同类相加
Fraction operator + (const Fraction& f){
return ...;
private:
int m_numerator;
int m_denominator;
}
Fraction f(3,5);
double a = f + 4;
1.当只存在Ⅱ时:编译器找到了+的重载,同时Fraction的构造除了第一个参数外都有默认值,允许隐式转换。会自动把4转换成 Fraction(4,1)。
2.当Ⅰ和Ⅱ同时存在:编译器会Error,ambiguous歧义。
3.当构造函数前加上“explicit”关键字,不允许隐式转换,f可以转换成double,但是“4”
转换不成Fraction,Error。
类的模仿分类:1.像指针;2.像函数
Pointer-like classes
1:智能指针:模板类+重构“*”,“()”操作符。
2:迭代器:模板类+重构“*”,“()”,“++”,“–”
function-like classes
仿函数类(重载"()")
namespace
命名空间,防止重名误解,所有自己定义的类都可以考虑加。
Funtion template 模板函数
模板会编译两次,第一次检查语法,第二次检查功能是否有实现。
编译器会自动根据实参类型推导。
template<class T> ≈ template<typename T>;
Member template 成员模板
模板特化\局部特化
当全特化和函数重载同时存在时,重载优先!
数量不定的模板参数(Variadic Templates)
Parameter pack(SINCE C++11)
A template parameter pack is a template parameter that accepts zero or more template arguments (non-types, types, or templates).
A function parameter pack is a function parameter that accepts zero or more function arguments.
A template with at least one parameter pack is called a variadic template.
参数包:
模板参数包是一个模板参数,它能包含0个或者多个(无类型模板参数类型模板参数\模板)模板参数。
函数参数包同上,只是能接收0个或者多个函数参数。
至少有一个参数包的模板称为数量不定的模板参数。
相关操作符:sizeof...()
typename
void print(){
}
template<typename T,typename... Types>
void print(const &T first,const &Type... args)
{
}
虚函数动态绑定,向上转型
因为,虚函数是以动态绑定的,因此,在向上转型后也是匹配的派生类的版本。
而派生类的其他方法和属性都被隐藏了,可以通过专门的虚函数来间接访问或者调用。
#include <iostream>
using namespace std;
class A {
public:
A() : m(20) {}
void show() { cout << "A show: " << m << endl; }
virtual void display() { cout << "A display: " << m << endl; }
private:
int m;
};
class B : public A
{
public:
B() : m(40) {}
void show() { cout << "B show: " << m << endl; }
virtual void display() { cout << "B display: " << m << endl; }
private:
int m;
};
int main()
{
// 定义上执行对象
A* a = new B;
// 调用静态绑定的方法show
a->show();
// 调用动态绑定的方法display
a->display();
return 0;
}
————————————————结果:
A show: 20
B display: 40