【20170710】C++ 初学踩过的一些坑总结

关于double与float

字面值默认是double, 加f才是float
如124412.0是double。124412.0f才是float
没特殊需要只用double(现代CPU可直接运算,性能与存储速度不比float慢),特殊情况考虑float和long double等

类型转换

1 / 2 = 0 // both int
1.0 /2 = 0.5 // double and int
1 / 2.0 = 0.5 // int and double

io流操作符setw()

setw()只对紧随其后的输出起作用,其他的操作如setfill()对随后的所有输出起作用。

cout << setfill('0')<< setw(5) << 233 << 666;

将输出

00233666

关于类的数据成员初始化代码位置问题

有且只有static成员可以且必须在类外初始化,非staitc只能在类内初始化。

非const不可以直接在声明后写初值
const数据成员不可以在构造函数体中赋值。
函数实现不管是否staitc/const可以在任何地方写函数体,但在类内会默认当做inline函数

乱七八糟的……画了个流程图结合下面示例看一下吧

流程图包含4种位置(类外初始化、声明处初始化、构造函数初始化列表处初始化、构造函数体赋值),没有出现的即为不可以。

其中构造函数体中代码,严格来讲不是初始化而是赋值,但是对于一些内置类型来说也相差无几。

关于类的数据成员初始化代码位置问题

类外初始化示例:

class c
{
private:
     static int x_;
public: 
     c(){}; 
};
static int c::x_ = 666; // correct

类内声明处初始化示例:

class c
{
private:
     const int x_ = 666; // correct
public: 
     c(){}; 
};

初始化列表示例:

class c
{
private:
     const int x_; 
public: 
     c():x_(666){}; // correct
};

函数体示例:

class c
{
private:
     int x_; 
public: 
     c():{
        this->x_ = 666; // correct
     }; 
};

堆上动态分配的类数据成员

应该定义拷贝构造函数与赋值运算符函数,否则按编译器默认行为可能造成出错(默认浅拷贝行为)

myClass(const myClass& anotherObject)
{
     // run when constructing object (this is still uninitialized)……
}

myClass& operator=(const myClass& anotherObject)
{
    // run when be assigned (this is already initialized)……
}

const对象与非const对象可以相互转换

 const int a =0;
 int b;
 b = a; //correct
 const int c = b; //correct

对于类来说,非const对象可以直接转换为const对象,而const对象转换为非const对象则需要对应的接收const对象的复制构造函数(默认会有一个,但是如果已有复制构造函数但不接收const对象则会导致无法转换)

友元函数

声明友元函数时,即使并在头文件中不使用具体类型,也要include具体文件以引入具体类型声明。

类继承体系

非虚函数不要在子类中重定义
原因:如果需要重定义,那么就不应该继承,子类与基类不构成is-a关系
非虚函数静态联编,使基类引用总调用基类函数,即使这个基类引用实际引用了一个派生类对象

从基类继承的带默认参数的函数,不要重定义其参数

重载函数注意事项

重载类构造函数

派生类永远在初始化列表中显式调用基类构造函数
复制构造函数还要注意参数要接收const对象以提供const对象到非const对象的转换

重载类型转换函数

没有返回值,且必须是非静态成员函数

//correct
myClass::operator double()
{
    return this.toDouble();
}

//error
static myClass::operator double()
{
    //……
}

//error
operator double(const myClass &object)
{
    return object.toDouble();
}

重载赋值运算符

永远应该返回*this以便使用于连续赋值
永远应该检查到自身的赋值以防错误
派生类永远显示调用基类赋值运算符,若基类没有重载赋值运算符,则应 static_cast<base_class>(*this)=rhs;
参数要接收const对象以提供const对象到非const对象的转换

const myClass& operator=(const myClass& rhs)
{
    base_class::operator=(rhs); // or static_cast<base_class>(*this)=rhs;
    if(this != &rhs)
    {
         // do something
    }
    return *this;
}
myClass c1,c2,c3;
c1 = c2 = c3;

运算符==与!=重载

总应该同时重载两者,且执行结果相反。

重载数组下标运算符

返回值类型需为非const引用类型,以便进行c[i]=xxx;的赋值操作

重载&& || ,

不要试图重载&& || ,
重载后他们便成为了普通函数,无法保证从左到右求值,&&和||使用的短路求值也无法保证。

异常处理

避免在构造函数中抛出异常
因为不会调用析构函数,而可能导致已经分配的资源无法释放,造成内存泄漏。
如果一定要抛出也要保证会安全释放,譬如使用智能指针。

catch类型最好是引用类型
对于类对象来说,引用不仅节约了拷贝对象的时间,而且防止可能异常对象会复制失败。
同时可以利用多态,而防止对象截断。

dynamic_cast操作的对象是类指针
bad_cast只有在无法相互转换的类进行dynamic_cast时才会抛出,如同级的互相转换。
如指向派生类对象的基类指针试图转换到另一派生类的指针,即试图向下类型转换(从父类转换为子类)失败,则会返回空指针而不会抛出bad_cast。

STL中异常基类及一系列派生类在<exception>中定义,而runtime_error与logic_erro还需要<stdexcept>

函数模板

在试图进行类型匹配时,不会进行类型转换。
即(T a, T b)只匹配(int, int)(double, double)等相同类型,而不会进行类型转换以匹配(int, double)
模板参数只接受编译期可确定的常量值

template <typename T>
void func(T a, T b)
{}
func(1, 2); //legal
func(1.0, 2.0); //legal
func(1.0, 2); //illegal

若为类成员函数,则需将定义放在头文件.h中以编译期实例化。

类模板

默认类型参数

可以为类模板指定默认类型参数

template <typename T = int>

但不能在函数模板中使用默认类型参数。

继承限制

类模板可以继承普通类。
类模板可以继承类模板。
普通类可以继承类模板的特化。
普通类不可以继承类模板。
总结:若子类为已实例化的类,则基类必须已实例化。

静态成员

每个模板实例类都有一份静态数据成员

嵌套类型

一个模板中的依赖于模板参数的名字被称为依赖名字
当一个 依赖名字 嵌套在一个类的内部时,我称它为 嵌套依赖名字
嵌套依赖名字必须显式使用typename关键字声明其为类型,否则将不被认为是类型而可能导致编译错误。

例如:

template<typename T> void f() {
    T::c* x;
   ...
}

这里没有使用typename声明,我们想表达的是:定义一个指针变量x,指向的数据类型是类T中的c类型。
然而编译器将认为是T中的静态成员变量c与已存在的变量x进行*运算相乘,此时编译出错。

如果要声明x为C内的nested dependent name(嵌套依赖名字),必须显式使用typename:

template<typename T> void f() {
    typename T::c* x;
   ...
}

Song - 「Last Of The Wilds」 - Nightwish

[player url=’//static.moew.xyz/usr/uploads/music/Nightwish/Last Of The Wilds.mp3’ title=”Last Of The Wilds” artist=’Nightwish’ origin=’http://music.163.com/#/m/song?id=4208408']
[/player]

[player url=’//static.moew.xyz/usr/uploads/music/Nightwish/Eramaan Viimeinen.mp3’ title=”Eramaan Viimeinen” artist=’Nightwish / Jonsu from Indica’ origin=’http://music.163.com/#/m/song?id=4208408']
[lrc]
[00:01.50]Erämaan viimeinen(Jonsu)
[00:51.40]Nukkuu korpi, ja nyt 荒野沉睡依旧
[00:53.78]aatoksissain minä matkaa teen 我穿行于思绪之中
[00:56.25]Yli metsien, uinuvan veen 穿越丛林,趟过静水
[00:58.73]Kuutar taivaan yön valaisee 月之女神指引着星空
[01:01.75]Maalaa maiseman, pohjoisen siimeksen 点缀北绿荫下的万物
[01:05.76]Unta onko tää 这是梦境吗?

[01:07.71]Vai kuolema jossa palata sinne saan missä 还是死亡 那片我向往回归的地方
[01:12.14]hiillos jo luovuttaa lumen alla 星火放弃希望 甘愿被冰雪埋葬
[01:15.33]Kun astun maailmaan, erämaan aikaan 当我踏足此地 如今的荒野……
[01:21.37]Ensilumi satoi kahdesti 初雪再次降临
[01:23.82]Maalasi sieluni taulun 散落在我灵魂的画卷上

[01:26.21]Tää jylhä kauneus ja ääretön yksinäisyys 这份狂放的美好与无尽的孤独
[01:31.21]Lapsuuteni metsän, taivaan 属于我儿时的森林和天空
[01:33.22]Kaikuu se haikeus halki tän matkan 那份美好贯穿整个旅途
[01:36.48]Aamun tullen yö tarinansa kertoo 当黎明破晓 黑夜诉说着它的传奇
[01:39.96]Jylhä on kauneus ja ääretön yksinäisyyteni 狂放即美好 无尽的孤独
[01:44.74]Sitä henkeensä halajaa 正是对灵魂的渴望
[01:47.13]Kehtoni hauta, hautani paikka 我摇篮中的坟墓 埋葬我的地方
[01:50.41]Erämaan viimeinen on 是荒野的最后一隅

[02:00.90]Minne katosivat muut 生机都到哪里去了?
[02:02.95]Suon noidat, neidontornit 沼泽女巫和她们的塔楼
[02:04.78]Ja varjoissa havisevat puut 还有那幽影笼罩的针叶林
[02:08.31]Kielon istutin ikihankeen 我在冰封中埋下铃兰的种子
[02:11.77]Ja hiljaisuuden tultua luotin tulevaan 当寂静降临 我只能企盼未来
[02:16.98]Ensilumi satoi kahdesti 初雪再次降临
[02:18.71]Maalasi sieluni taulun 散落在我灵魂的画卷上

[02:21.79]Tää jylhä kauneus ja ääretön yksinäisyys 这份狂放的美好与无尽的孤独
[02:25.77]Lapsuuteni metsän, taivaan 属于我儿时的森林和天空
[02:28.77]Kaikuu se haikeus halki tän matkan 那份美好贯穿整个旅途
[02:32.21]Aamun tullen yö tarinansa kertoo 当黎明破晓 黑夜诉说着它的传奇
[02:35.55]Jylhä on kauneus ja ääretön yksinäisyyteni 狂放即美好 无尽的孤独
[02:40.46]Sitä henkeensä halajaa 正是对灵魂的渴望
[02:42.66]Kehtoni hauta, hautani paikka 我摇篮中的坟墓 埋葬我的地方
[02:46.04]Erämaan viimeinen on 是荒野的最后一隅

[03:59.54]Tää jylhä kauneus ja ääretön yksinäisyys 这份狂放的美好与无尽的孤独
[04:03.33]Lapsuuteni metsän, taivaan 属于我儿时的森林和天空
[04:05.96]Kaikuu se haikeus halki tän matkan 那份美好贯穿整个旅途
[04:09.57]Aamun tullen yö tarinansa kertoo 当黎明破晓 黑夜诉说着它的传奇
[04:13.04]Jylhä on kauneus ja ääretön yksinäisyyteni 狂放即美好 无尽的孤独
[04:17.81]Sitä henkeensä halajaa 正是对灵魂的渴望
[04:20.07]Kehtoni hauta, hautani paikka 我摇篮中的坟墓 埋葬我的地方
[04:23.49]Erämaan viimeinen on 是荒野的最后一隅
[04:27.29]Tää jylhä kauneus ja ääretön yksinäisyys 这份狂放的美好与无尽的孤独
[04:30.91]Lapsuuteni metsän, taivaan 属于我儿时的森林和天空
[04:33.88]Kaikuu se haikeus halki tän matkan 那份美好贯穿整个旅途
[04:37.40]Aamun tullen yö tarinansa kertoo 当黎明破晓 黑夜诉说着它的传奇
[04:40.88]Jylhä on kauneus ja ääretön yksinäisyyteni 狂放即美好 无尽的孤独
[04:44.78]Sitä henkeensä halajaa 正是对灵魂的渴望
[04:49.08]Kehtoni hauta, hautani paikka 我摇篮中的坟墓 埋葬我的地方
[04:52.03]Erämaan viimeinen on 是荒野的最后一隅
[/lrc]
[/player]

[player url=’//static.moew.xyz/usr/uploads/music/如我西沉.mp3’ title=”如我西沉” artist=’满汉全席’ origin=’http://music.163.com/#/m/song?id=450532474']
[/player]

【2016.5.08】C++ 学习笔记

异常规范

异常规范是在 C++11 中弃用的 C++ 语言功能。这些规范原本用来提供有关可从函数引发哪些异常的摘要信息,但在实际应用中发现这些规范存在问题。 证明确实有一定用处的一个异常规范是throw() 规范。
例如: cpp void MyFunction(int i) throw();
告诉编译器函数不引发任何异常。它相当于使用 __declspec(nothrow)。这种用法是可选的。
(C++11) 在 ISO C++11 标准中,引入了 noexcept 运算符,该运算符在 Visual Studio 2015 及更高版本中受支持。尽可能使用
noexcept 指定函数是否可能会引发异常。

简单地总结上面的废话,就是本来throw(xxx)可以指定函数可能引发啥异常,但是后来人们发现啥异常没卵用,唯一有用的用法就是用throw()指定不会引发任何异常,而这种唯一有用的异常规范用法在C++11中被noexcept替代掉了,异常规范也直接废弃了。

STL

vector, list, deque 都是顺序容器
vector, deque 支持对任意元素的访问,元素严格有序
list, set, multiset, map, multimap 支持bidirectional iterator,可以++iter –iter
stack, queue, priority_queue 不支持迭代器
map 和 set 中不允许存在关键字相同的元素(multimap和multiset可以)
所有的容器都有拷贝构造函数
输出迭代器只能够向一个序列写入数据,它可以被修改和引用,不能用作右值(不能读取值)
调用容器的 erase(beg, end) 函数,会删除容器中的 从 beg 开始(包含beg) 到 end 结束(但不包含end) 的所有元素
clear
swap 交换内容
clear 清空元素
assgin(n, elem) assgin(begin, end) 清空并且加上那堆元素

  • Copyrights © 2014-2022 听寒
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信