1.如果一个类指定了友元类,则友元类的成员函数可以访问此类包括非公有成员在内的所有成员,友元函数也可以在类的内部定义,这样的函数是隐式内联的。
2.直到类被定义之后数据成员才能被声明成这种类类型。换句话说我们必须首先完成类的定义,然后编译器才能知道存储该数据成员需要多少空间。因为只有当类全部完成后类才算被定义,所以一个类的成员类型不能是类自己。但是一旦一个类的名字出现以后,他就被认为是声明过了(但未定义),因此类允许包含指向它自身类型的指针或者引用:
class Link_screen
{
Screen window;
Link_screen *next;
Link_screen *prev;
};
3.名字查找与类的作用域
编译器处理完类中的全部声明后才会处理成员函数的定义
4.构造函数的初始化
如果是const引用或者属于某种未提供默认构造函数的类类型,我们必须通过构造函数初始值列表为这些成员提供初始值。
构造函数的初始值有时必不可少
如果成员是const或者是引用的话,必须对他进行初始化,而不是进行赋值。
class constref{
public:
constref(int ii);
private:
int i;
const int ci;
int &ri;
}
如果我们没有提供构造函数而是像下面这样写的话出错
constref::constref(int ii)
{
i = ii; //正确
ci = ii; //错误 不能给const 赋值
ri = i; //错误 ri没有被初始化
}
正确的方式应该是
constref::constref(int ii):i(ii),ci(ii),ri(i){}
5.静态成员可以适用于某些场景而普通成员不能
静态数据成员可以是不完全类型
class Bar
{
public:
//..
private:
static Bar mem; //正确:静态成员可以是不完全类型
Bar *mem1; //正确:指针成员可以是不完全类型
Bar mem2; //错误:数据成员必须是完全类型
}
6.
静态成员作为默认实参
class Screen
{
private:
static const char bkground;
public:
Screen& clear(char = bkground);
}
7.类内只能初始化整型类型的静态常量,所以不能在类内初始化vec。
.h文件中
class E{
static double rate = 6.5; //错误 应该为类内静态成员变量 static constexpr double rate = 6.5;
static const int vecsize = 20;
static vector<double>vec(vecsize); //类内只能初始化整型静态变量,
}
.c文件中
double E::rate;
vector<double>E::vec; //应该为vector<double> Example::vec(Example::vecSize);
8.Io对象无拷贝或者赋值
由于不能拷贝IO对象,因此我们也不能将形参或者返回类型设置为流类型。进行IO操作通常是以引用的方式传递和返回流,读写一个IO对象会改变其状态,因此传递和返回的引用不能是const的
9.IO库
10.begin
返回的是普通迭代器,cbegin
返回的是常量迭代器。当不需要写访问时,应使用cbegin和cend;
11.容器元素是拷贝
当我们用一个对象来初始化容器的时候,或将一个对象插入到容器中时,实际上放入到容器中的是对象值的一个拷贝,而不是对象本身。就像是我们将一个对象传递给非引用的参数一样,容器中的元素与提供值的对象之间没有任何联系。随后对容器中的任何元素的改变都不会影响到原始对象,反之亦然。
12、访问成员函数返回的是引用
假定C是一个vector
vector<int>c;
if(!c.empty())
{
c.front() = 42; //将42赋予C中的第一个元素
auto &v = c.back(); //获得指向最后一个元素的引用
v = 1024; //改变c中的元素
auto v2 = c.back(); //v2不是一个引用,他是c.back()的一个拷贝
v2 = 0; //这样操作并没有影响c中的值
}
13.不要保存end返回的迭代器
auto begin = v.begin();
end = v.end(); //保存尾迭代器是一个坏主意
while(begin!=end)
{
++begin;
begin = v.insert(begin,42);
++begin;
}
此代码是未定义的,会导致死循环,问题在于我们将end操作返回的迭代器保存在一个名为end的局部变量中。当我们向循环中添加元素的时候,迭代器已经失效,所以在deque、string或者vector中的元素,不要缓存end 返回的迭代器
14.编写程序,从一个vector初始化一个string。
vector<char> v{ 'h', 'e', 'l', 'l', 'o' };
string str(v.cbegin(), v.cend());
15. map and set
map 是键值对,而 set 只有键没有值。当我需要存储键值对的时候使用 map,而只需要键的时候使用 set。
set 是有序不重复集合,底层实现是红黑树,而 list 是无序可重复集合,底层实现是链表。
unordered_map,它也是由哈希表实现的,而map是用红黑树实现的。
16.可以定义一个vector<int>::iterator 到int 的map 吗?list<int>::iterator 到int的map 呢?
答:可以定义一个vector 的map,但是不可以用list,因为map是排序的,键一定满足<操作,而list不满足此操作。
17const 放在一个函数的前面和后面的区别
1、int GetY() const;
2、const int * GetPosition();
对于1
该函数为只读函数,不允许修改其中的数据成员的值,也不允许调用非const成员函数。
对于2
修饰的是返回值,表示返回的是指针所指向值是常量。
18.insert的返回值
map<string,size_t>word_count; //从string到size_t的空map
string word;
while(cin>>word)
{
//插入一个元素,关键字等于word,值为1;
//若word已经在word_count中,insert什么都不做
auto ret = word_count.insert({word,1});
if(!ret.second) //word已经在word_count中
++ret.first->second; //递增计数器
}
其实等价于定义
pair<map<string,size_t>::iterator,bool>ret = word_count.insert(make_pair(word,l));
定义了一个pair,第二类型为bool,第一个类型是在map<string,size_t>类型上定义的iterator类型。
若insert成功:先添加一个元素,然后返回一个 pair,pair 的 first 元素是一个迭代器。这个迭代器指向刚刚添加的元素,这个元素是 pair ,然后递增 pair 的 second 成员。 若insert失败:递增已有指定关键字的元素的 second 成员。
19.