尽量以const,enum,inline 代替#define
{
const int arr_len = 100;
int arr[arr_len]{ 0 };
}
{
enum {
arr_len=100
};
int arr[arr_len]{ 0 };
}
尽可能使用引用传递,减少析构和构造
确定使用对象前已经被初始化
(1)使用成员初始列初始化对象 因为不这样的话构造函数会执行默认的构造,然后进行赋值操作 (2)解决垮单元初始化问题,以local static 代替no_local static
为多态基类声明vitrul 析构函数
但是vitrul会导致产生vptr,增加了对象的大小 通常是如果没有vitrual函数,则不必要析构vitrual 还有就是不派生STL的类
不让析构函数产生异常
虚函数不能再构造和析构中使用
如果自己写copying 函数,记得copying完
返回对象别妄想返回refrence
local对象会随函数的结束而销毁
将成员变量声明为private
因为一旦很多地方使用了该变量,一旦要撤销该变量时会很麻烦,例如修改函数名称啥的
尽量使用no_member ,no_friend,代替member函数,有利于封装
尽可能延后变量定义的出现
变量会占内存,同时要考虑析构和构造带来的执行效率
尽量少做转型动作
const_cast(expression) 会将expression的const转变为no_const dynamic_cast() “向下安全转型”,增加了运行的成本 reinterpret_cast()低级转型,实际的动作和结构取决于编译器,不利于移植 static_cast() 隐式强迫转型,但是不能将const --> no_const
正确认识inline
inline 只是向编译器申请而不是命令,成功与否取决于编译器 inline 省去了函数调用的时间,但是会增加体积 在class 头文件中class声明时就实现的成员函数,默认inline 其他的在声明时inline但是实现得在头文件中
将文件间的编译依存关系降至最低
每个头文件尽可能满足自身的需要就,而不是引入其他头文件 可以分为两步:
步骤1.将需要的数据类型前置声明
#pragma once
#ifndef MANAGER_H_
#define MANAGER_H_
#include <memory>
class TC1;
class TC2;
class Manager{
std::shared_ptr<TC1> m_tc1;
std::shared_ptr<TC2> m_tc2;
public:
void doSomething();
};
#endif
或者
exten class TC1;
exten class TC2;
步骤2:在实现的文件引入相应的数据类型的定义
#include "Manager.h"
#include "TC1.h"
#include "TC2.h"
void Manager::doSomething(){
m_tc1 = std::make_shared<TC1>();
m_tc2 = std::make_shared<TC2>();
m_tc1->start();
m_tc2->start();
}
确定public继承是is-a 关系
继承时注意不要遮掩继承而来的名称
举个会遮掩的例子
#pragma once
#ifndef TC1_H_
#define TC1_H_
class TC1{
public:
TC1();
~TC1();
void start();
void fun1(int);
void fun1();
void fun2(int);
void fun2();
};
#endif
#pragma once
#ifndef MANAGER_H_
#define MANAGER_H_
#include <memory>
#include "TC1.h"
class Manager:public TC1{
void fun1();
public:
void doSomething();
};
#endif
void Manager::doSomething(){
fun1();
fun1(1);
}
还存在这样的一种情况,Driver不想继承Base的全部函数,也就是客户使用时候并不能使用base的所有的函数,那样继承的时候可以private, 但是吧还是有想使用的函数,考虑到使用using::Base::fun,很多会引入很多相同名字的函数,并不只是一个
#pragma once
#ifndef TC1_H_
#define TC1_H_
class TC1{
public:
TC1();
~TC1();
void start();
void fun1(int);
void fun1();
void fun2(int);
void fun2();
};
#endif
#pragma once
#ifndef MANAGER_H_
#define MANAGER_H_
#include <memory>
#include "TC1.h"
class Manager:private TC1{
public:
using::TC1::fun1;
void fun1();
};
#endif
int main(int argc, char** argv) {
Manager m;
m.fun1();
m.fun1(1);
return 0;
}
这时我们只想引入其中的一个函数,但是我们可以使用的是两个 解决的办法是转接函数,而不是用using::TC1::fun1
#ifndef MANAGER_H_
#define MANAGER_H_
#include <memory>
#include "TC1.h"
class Manager:private TC1{
public:
void fun1();
};
#endif
#include "Manager.h"
#include "log/LogX.h"
#include "TC1.h"
#include "TC2.h"
void Manager::fun1(){
TC1::fun1();
}
int main(int argc, char** argv) {
Manager m;
m.fun1();
return 0;
}
接口和缺省函数应该分离开来
(1)Base的vitrual 函数(先称为接口)一般需要Driver重新定义,但是其实现可以直接使用缺省的函数,但是原则上得分离开来,因为,很多情况,缺省得并不是我们想要的,而客户又使用了缺省,此时我们不得不思考怎们让Driver必须重新定义该接口.
常规得解决办法是,在虚函数后面加上=0 让此函数变成纯虚函数
class Base {
public:
virtual void fun1() = 0 {
LOGXA("default function");
}
};
class Driver :public Base {
public:
virtual void fun1() {
LOGXA("driver function");
}
};
int main(int argc, char** argv) {
Driver d;
return 0;
}
除了纯虚函数以外,可以使用得方法还有很多,比如增加一个额外得缺省函数,让此函数在Driver里面重新定义,(这方法不太好)
class Base {
public:
void fun1() {
defaultFun1();
}
private:
virtual void defaultFun1() {
LOGXA("default fun");
}
};
class Driver :public Base {
public:
void defaultFun1() {
LOGXA("driver fun");
}
};
int main(int argc, char** argv) {
Driver d;
d.fun1();
return 0;
}
result:
[+] log construction, can use it E:\code\Test\_log\log.html
[0]06:45:29[info][988] [Driver::defaultFun1] driver fun (TestMain.cpp:40)
[+] ~log deconstruction
还有一种方法就是fun1(fun* f=default) 这样得策略模式,让driver自己实现f
绝不重定义继承而来的no_vitrual函数
绝不修改继承而来的缺省参数值
将与参数无关的代码抽离templeta
template 需要类型转换时 请将模板定义成非成员函数
加上friend
template对类型判断后执行的方法
1.可以利用重载 2.if else 利用typeid
|