原因:
新增安全检查
如:打开一个文件不存在或没有权限,或访问链接不可用等,为运行时错误;
为此,我们需要输出错误信息:输出错误的原因
,错误的地方
,相关的变量值
,并采取适当的操作
;
使用该宏,当条件为假
时即可触发,将错误信息
输出;
#define ASSERT(condition, msg) \
if(!(condition)) { \
std::cerr << "in file: "__FILE__; \
std::cerr << " #" << __LINE__ << ": "; \
std::cerr << msg; \
exit(1);\
}
int main() {
ASSERT(0 > 1, "test");
}
// 示例:
/**
ASSERT(index < array.size(), "Index " << index << " is out of bounds " << array.size());
输出足够的信息,让该错误信息更有意义;
*/
生产
使用和测试
使用,当不需要时,则可以将其关闭,从而不降低程序的执行效率;开发者
来决定,需要考虑该处使用安全检查其调用频率
、执行时间
(条件的执行时间)等;一同编写
安全检查,而不是过后在做补充;#define TEST_ASSERT_ON // 若定义就开启
#ifdef TEST_ASSERT_ON
#define TEST_ASSERT(condition, msg) ASSERT(condition, msg)
#else
#define TEST_ASSERT(condition, msg)
#endif
#define ASSERT(condition, msg) \
if(!(condition)) { \
std::cerr << "in file: "__FILE__; \
std::cerr << " #" << __LINE__ << ": "; \
std::cerr << msg; \
exit(1);\
}
int main() {
TEST_ASSERT(0 > 1, "test");
}
测试
时,立即中断程序,修改后继续尝试;错误处理函数
,并在顶层
代码中捕捉
该异常,将其记录在日志
文件中,不会干扰到其他功能的正常运行;// 一
#ifdef THROW_EXCEPTION_ON_BUG
throw std::logic_error("error");
#else
std::cout << "exit" << std::endl;
exit(1);
#endif
// 二
int main() {
try {
double stock_price{ 100.0 };
ASSERT(
0 < stock_price && stock_price <= 1e6,
"Stock price " << stock_price << " is out of range.");
stock_price = -1.;
ASSERT(
0 < stock_price && stock_price <= 1e6,
"Stock price " << stock_price << " is out of range.");
}
catch (const std::exception& ex) {
std::cerr << "Exception caught in " << __FILE__
<< " #" << __LINE__ << ":\n"
<< ex.what() << std::endl;
}
return 0;
}
》》》》》》》》参考sylar《《《《《《《《
std::vector<std::string>& Backtrace(int size, size_t skip) {
void **array = (void**)malloc(sizeof(void*) * size);
size_t s = ::backtrace(array, size);
std::vector<std::string> rt;
char** str = backtrace_symbols(array, s);
if(str == nullptr) {
std::cerr << "backtrace_symbols error" << std::endl;
return;
}
for(size_t i=skip; i<s; ++i) {
rt.push_back(str[i]);
}
free(str);
free(array);
}
std::string BacktraceToString(int size, size_t skip, const std::string& prefix) {
auto vc = Backtrace(size, skip);
std::stringstream ss;
for(size_t i=0; i<vc.size(); ++i) {
ss << prefix << vc[i] << std::endl;
}
return ss.str();
}
#define ASSERT(condition, msg) \
if(!(condition)) { \
std::cerr << "in file: "__FILE__; \
std::cerr << " #" << __LINE__ << ": "; \
std::cerr << BacktraceToString(100, 2, " ") \
std::cerr << msg; \
exit(1);\
}
静态
或动态
分配的数组,改用array
或vector
模板;[]
的new和delete操作符,让vector
模板为多个元素分配内存;增加安全检查
的进行重写;()
访问元素,并提供越界检查;若出现越界
的情况,则会返回错误的信息;但对于vector提供了at
使用,该方法能够抛出一个out_of_range
异常,但会降低代码的效率;
故,我们可以自己捕捉目标
并改写
;
namespace scpp {
template <typename T>
class vector : public std::vector<T> {
public:
typedef unsigned size_type;
explicit vector(size_type n = 0)
: std::vector<T>(n) { }
vector(size_type n, const T& value)
: std::vector<T>(n, value) { }
template <class InputIterator> vector(InputIterator first,
InputIterator last)
: std::vector<T>(first, last) { }
T& operator[] (size_type index) {
std::stringstream stringStream;
SCPP_TEST_ASSERT(index < std::vector<T>::size(),
stringStream << "Index " << index
<< " must be less than "
<< std::vector<T>::size());
return std::vector<T>::operator[](index);
}
const T& operator[] (size_type index) const {
std::stringstream stringStream;
SCPP_TEST_ASSERT(index < std::vector<T>::size(),
stringStream << "Index " << index
<< " must be less than "
<< std::vector<T>::size());
return std::vector<T>::operator[](index);
}
};
}
vector注意事项
缓慢
,故我们需要预先
给vector分配内存;vector<int> vec(n, 0); // 具有初始化的内存分配
vector<int> vec; // 无初始化的内存分配
vec.reserve(n);
从vector中派生新类的注意事项
若基类的析构函数不为虚函数,则在多态使用时则子类的析构函数不会被执行到;
class Base{
public:
~Base(); // 非虚拟
};
class Derived : public Base {
public:
~Derived() {} // 非虚拟
};
Base* p = new Derived;
delete p; // 此时delete调用的是基类的析构,不会调用子类的析构
堆栈
上分配内存,而vector是通过new
分配,速度较慢些,但最好使用array
模板(静态数组);低效
的;()
对元素进行访问,由于[]操作符只能接受1个参数;template<typename T>
class matrix {
public:
typedef unsigned size_type;
matrix(size_type rows, size_type cols)
:m_rows(rows), m_cols(cols) {
ASSERT(m_rows > 0, "rows must be positive");
ASSERT(m_rows > 0, "cols must be positive");
}
matrix(size_type rows, size_type cols, const T& init_val)
:m_rows(rows), m_cols(cols), m_data(m_row*m_cols, init_val) {
ASSERT(m_rows > 0, "rows must be positive");
ASSERT(m_rows > 0, "cols must be positive");
}
size_type getRows() const { return m_rows; }
size_type getCols() const { return m_cols; }
T& operator() (size_type row, size_type col) {
return m_data[index(row, col)];
}
const T& operator() (size_type row, size_type col) const {
return m_data[index(row, col)];
}
private:
size_type index(size_type row, size_type col) const {
ASSERT(row < m_rows, "Row " << row << " must be less than " << m_rows);
ASSERT(col < m_cols, "Col " << col << " must be less than " << m_cols);
return col * row + col;
}
private:
size_type m_rows;
size_type m_cols;
std::vector<T> m_datas;
};
指针运算
,使用vector
模板或数组
索引;#include
#include
int main() {
std::vector<int> v;
for (int i = 0; i < 10; ++i) v.push_back(i);
int* old_vptr = &v[3];
int& old_ref = v[3];
std::vector<int>::const_iterator old_iter = v.begin() + 3;
std::cout << "vptr: " << *old_vptr << " ref: " << old_ref << " addr: " << old_vptr << std::endl;
std::cout << "add elements..." << std::endl;
for (int i = 0; i < 100; ++i) v.push_back(i*10);
std::vector<int>::const_iterator new_iter = v.begin() + 3;
std::cout << "vptr: " << *old_vptr << " ref: " << old_ref << " addr: " << &v[3] << std::endl;
return 0;
}
template<typename T>
class TNumber {
public:
TNumber(const T& x = 0) : m_data(x) {}
operator T() const { return m_data; }
TNumber& operator=(const T& x) {
m_data = x;
return *this;
}
TNumber operator++(int) {
TNumber<T> copy(*this);
++m_data;
return *this;
}
TNumber& operator++() {
++m_data;
return *this;
}
TNumber& operator+=(T x) {
m_data += x;
return *this;
}
TNumber& operator-=(T x) {
m_data -= x;
return *this;
}
TNumber& operator*=(T x) {
m_data *= x;
return *this;
}
TNumber& operator/=(T x) {
ASSERT(x!=0, "Attmpt to divide by 0");
m_data /= x;
return *this;
}
T operator/(T x) {
ASSERT(x != 0, "Attmpt to divide by 0");
return m_data / x;
}
private:
T m_data;
};
typedef TNumber<int> Int;
...
class A {
public:
Int m_a; // 此时即可被初始化
};
class Bool {
public:
Bool(bool x = false)
:m_data(x) {}
operator bool() const { return m_data; }
Bool& operator=(bool x) {
m_data = x;
return *this;
}
Bool& operator&=(bool x) {
m_data &= x;
return *this;
}
Bool& operator |= (bool x) {
m_data |= x;
return *this;
}
private:
bool m_data;
};
std::ostream& operator<<(std::ostream& os, Bool val) {
if (val) {
os << "True";
}
else {
os << "False";
}
return os;
}
》》》》》》》》内存泄漏参考链接《《《《《《《《
内存泄漏:实际是被分配的内存的所有权丢失;
对象的所有权:删除该内存的责任;
可能出现内存泄漏的原因
注意事项
该类型的指针销毁时刻为最后消亡的那个对它进行删除;
》》》》》》》》智能指针参考链接《《《《《《《《
该类型指针存在的问题
当使用一个函数来获取一个指针时,可能会出现的潜在错误,避免对象的所有权不明确等原因,让函数返回一个智能指针;
std::shared_ptr<MyClass> MyFactoryClass::Create(const Input& inputs);
// or
void MyFactoryClass::Create(const Inputs& inputs, auto_ptr<MyClass>& result);
当我们试图解引用NULL指针时,将会导致程序崩溃,为此以下提供一种不删除它所指向对象的半智能指针;
template<typename T>
class Ptr {
public:
explicit Ptr(T* p = nullptr)
: m_ptr(p) {}
T* Get() const { return m_ptr; }
Ptr<T>& operator=(T* p) {
m_ptr = p;
return *this;
}
T* operator->() const {
ASSERT(m_ptr != nullptr, "Attempt to use operator -> on NULL pointer.");
return m_ptr;
}
T& operator*() const {
ASSERT(m_ptr != nullptr, "Attempt to use operator -> on NULL pointer.");
return *m_ptr;
}
private:
T* m_ptr;
};
上述指针的特性
何时需要在析构中编写
比较有实质性的做法可能就是在析构中对类中资源的释放,但该做法可能交给智能指针去处理并不需要交给我们手动处理;
交给智能指针的好处
> < <= >= == !=
;
class MyClass {
public:
int CompareTo(const MyClass& that) const;
public:
#define SCPP_DEFINE_COMPARISON_OPERATORS(Class) \
bool operator<(const Class& that) const { return CompareTo(that) < 0; } \
bool operator>(const Class& that) const { return CompareTo(that) > 0; } \
bool operator<=(const Class& that) const { return CompareTo(that) <= 0; } \
bool operator>=(const Class& that) const { return CompareTo(that) >= 0; } \
bool operator!=(const Class& that) const { return CompareTo(that) != 0; } \
bool operator==(const Class& that) const { return CompareTo(that) == 0; } \
};
若代码中的处理方法可以有多种,可以选择在debug中和release中进行区分;
#ifdef DEBUG
// 处理1
#else
// 处理2