C++88个注意点子之51~70

来源:互联网 发布:美工地坪漆 编辑:程序博客网 时间:2024/05/16 17:59

51. 容器适配器的初始化

stack和queue都默认使用deque容器实现,将deque容器作为实现的底层数据结构。而priority_queue则在vector上面实现的。那么,我们如何来修改stack和queue这些适配器的实现结构呢? 比如我们将stack栈适配器用vector动态数组结构来实现,这样定义: stack< string, vector<string> > strStack;这样就定义了一个栈结构,元素是string的,然后用vector数组来实现,这里注意在类型<>中再使用<>时,要注意用空格隔开有可能连在一起的>>,因为这样编译器就不知道该如何解释了,会与右移运算符搞混。下面来看看stack, queue, priority_queue可以用哪些结构实现:

stack: vector deque(default) list

queue: deque(default) list

priority_queue: vector(default) deque

此外,还有一点需要注意的是,要用基础容器初始化适配器时,必须和适配器类型相同:

stack<int> b(a); a的类型必须是deque<int>的。


52. pair类型  pair<int, string>

map类型  map<int, string>

map中元素为pair类型, 初始化中元素类型必须是用pair<k, v>:

map<k, v> m(b, e);


53. 对于map的键类型,必须支持'<'运算,若自己定义的类,则需要自己实现<运算。

在map中下标插入元素会带来不必要的初始化。

吐槽一下C++中的map类,用起来真心没有java中的map用起来方便。。。


54. 在看别人的源码时,经常会在程序开头出现# pragma once 和 # ifndef # endif这些东西。下面梳理一下:

(1) #pragma once 可以保证文件只被包含一次,但相同内容文件则不能保证

(2) #ifndef _ClassName_H

# define _ClassName_H

.....(class define)

#endif

可以保证同一个文件且相同内容文件只被包含一次(受C/C++标准支持)

本人比较喜欢用第二种,但是这两种方法各有利弊,具体的可以自行了解。


55. 稳定排序与一般排序

在C++中stable_sort()是稳定排序, 而sort()不是稳定的,这两个函数用法都是一样的。


56. 在C++类中也可以定义自己的局部类型名字。

public:

      typedef std::string::size_type index;

在public中定义,则不仅类中成员可以使用,类外用户也可以使用。

也可以定义在private中,仅供类内使用。


57. 在类内部定义的成员函数,将自动作为inline处理。也可以显式地将成员函数声明为inline;

inline get_cursor() const;

在类外定义: inline 返回类型名 Screen::get_cursor() const;


58. 一般的类定义体都是放在头文件,用#ifndef保护,然后具体实现放在头文件外面。


59. 类声明(前向声明,在定义前)是不完全类型,只能用于指向该类型的指针及引用,或者用于声明使用该类型作为形参类型或返回类型的函数。

class LinkScreen {      Screen windows;       LinkScreen *next;       LinkScreen  *prev;}

60. 在普通的非const成员函数中,this类型是一个指向类类型的const指针。而在const成员函数中,this是一个指向const类类型对象的const指针。对于返回*this,要格外注意。

Screen& display() {return *this;}

const Screen& display() const {return *this;}

这个两个是不一样的函数,重载实现。


61. 构造函数执行分为二个阶段:

(1) 初始化列表初始化 

(2) 构造函数体初始化赋值

对于一般内置类型,初始化列表初始化和赋值性能一样。

但是对于类类型,初始化阶段会调用默认构造函数,再赋值,性能会损失。对于const成员和引用成员一定要在初始化列表中初始化,因为它们必须在初始化时赋值。

注意:对const或引用或没有默认构造函数的类类型,使用初始化式。


62. C++中构造函数初始化列表顺序无关紧要,重要的是变量定义的次序,按照变量定义顺序来初始化。


63. C++中可以用单个实参来调用的构造函数,隐含了从形参类型到类类型的一个隐式转换。

Sales_item(const std::string &book="");

在外调用含有Sales_item类型的函数时,可以直接传入一个字符串,编译器会帮我们自动的用Sales_item单形参构造函数构造出一个匿名类。有的时候这种隐式转换会导致错误。为了程序的严谨性,我们应该用explict关键词来抑制由构造函数定义的隐式转换。

注意:explict只能用于类内部的构造函数声明上,在外部定义函数处不能加explict。


64. 除非有明显的理由想要定义隐式转换,否则,单形参构造函数应该为explict。显示使用构造函数string("hello"), Sales_item("124"), vector<int>(3) , 有点类似于强制类型转换。


65. 友元,允许一个类将其非公有成员的访问权授予指定的函数或类。授予类: friend class WindowMgr;

授予函数: friend 返回值 别类::函数();

注意:友元声明应该成组地放在类定义的开始或结尾。


66. static类型

面向过程式中,

(1) 全局static变量(不能被其它文件使用)

(2) 局部static变量

(3) static 函数(不能被其它文件所用, 同时在其它文件中定义相同名字,不会发生冲突)

面向对象中,

(1) 静态数据成员, 遵循公有私有等规定用法,不能再构造函数中初始化,需要在类定义外初始化将static去掉。

可用类名直接访问, Point::size;

(2) 静态成员函数,无this指针,且只能对static数据成员操作,且能调用其他静态成员函数,在类体外定义,去掉static名。

注意:static型的数据都存储在程序的全局数据区中。

(3)特殊的整型const static 成员

必须在类体中初始化,但在类定义体外定义。(无须初始化)

(4) static成员不是类对象的组成部分。对于非static成员被限定声明为其自身类对象的指针或引用。而static成员类型可以是该成员所属的类类型。static数据成员可用作默认实参(在类中)。

我只想说一句:C++中的static规则还真是多啊,要记住不容易,需要多加练习。


67. 复制构造函数在下面几种情况中被调用:

(1) 对象的定义形式中

(2) 形参与返回值

(3) 数组元素容器初始化

合成复制构造函数:对于类类型调用其复制构造函数,而内建类型逐个成员复制到正创建对象。若为数组,则依次复制数组中每个元素。

Foo(const Foo&);


68. 可以使用合成复制构造函数的类也可以使用合成赋值操作符。若类需要复制构造函数,它也需要赋值操作符。若类中有指针成员,则一般需要显式指定复制构造函数和赋值操作符。

复制构造函数: SalesItem(const SalesItems&);

赋值操作符 SalesItem& SalesItems::operator=(const SalesItems &);


69. 析构函数当作用域或指向对象指针删除时才启动。注意:如果类需要析构函数,则它也需要赋值操作符和复制构造函数。

与复制构造函数和赋值操作符不同的是编译器总是为我们合成一个析构函数,逆序撤销每个非static成员。即使我们编写了析构函数,合成析构函数仍然运行。


70.合成复制构造函数默认只复制指针成员的地址,而不会复制指针指向的对象。3种处理办法:

(1) 指针成员会采用常规指针型行为。

(2) 类可以实现所谓的"智能指针"行为。

(3) 类采取值型行为。(复制指向的对象)

















0 0
原创粉丝点击