C++ 面试知识点
不定期整理一些C++的知识点
C++是静态类型,即在编译阶段检查类型
引用和const变量必须被初始化
多态是通过虚函数实现的,虚函数的实现机制是虚函数表,虚函数表一般存放在内存的最头部
const对象不能调用类中的非const成员函数,将成员变量定义为mutable(可变数据成员)可以使其在const成员函数中被修改(其作用是使成员变量对于外部类来说不能修改,但内部可以对其进行修改)
struct的默认访问权限是public,class则是private
解引用迭代器来获得它指向的元素(*itr),it->mem() == (*it).mem()
cout << *iter++ << endl; == cout << *(iter++) << endl;这种写法更加简洁,应该多使用
定义数组形参:
void print(int*, size_t size);
void print(const int[], size_t size);
void print(const int[10], size_t size); //因为无法判断数组的长度,所以需要把长度也作为形参
void print(int (&arr)[10]); //数组的引用作为形参必须加括号, 且数组确定
void print(int (*matrix)[10], int rowSize); //传入多维数组也必须加括号, 且第二维长度确定
void print(int matrix[][10], int rowSize);
使用initializer_list<T>来传入不定数量的形参:
void error_msg(initializer_list<string> il)
{
for (auto beg = il.begin(); beg != il.end(); ++beg) {
/* Do something */
}
}
调用返回引用的函数得到一个左值
函数返回数组: auto func(int i) -> int(*)[10]; //c++11, 尾置返回类型
函数指针声明:
bool compare(const string&, const string&);
bool (*pf)(const string&, const string&);
pf = compare;
bool b1 = pf("hello", "world");
void useFunc(const string& s1, const string& s2, bool pf(const string&, const string&));
useFunc("hello", "world", compare);
auto fl(int) -> int (*)(int*, int*);
编译器为类对象生成的四个默认函数:构造函数,析构函数,拷贝构造函数,赋值运算符
vector便于访问,list便于插入删除
向泛型算法中传递谓词
bool isShorter(const string& s1, const string& s2) {
return s1.size() < s2.size();
}
sort(words.begin(), words.end(), isShorter);
lambda函数
[capture list](parameter list)->return type {function body}, 其中参数列表和返回类型可忽略
auto f = [] { return 42; };
stable_sort(words.begin(), words.end(), [](const string& s1, const string& s2){ return s1.size() < s2.size() });
size_t sz = 100;
auto fz = [sz](const string& s1){ return s1.size() < sz }; // 函数拷贝捕获列表里的值而不是引用。
auto fs = [=, &](const string& s2){ return s1.size() < sz; } // 隐式捕获,=值捕获,&引用捕获
map会自动排序,无序关联容器使用unordered_map。map的底层实现是红黑树或AVL树,unordered_map的底层实现是哈希表
使用make_pair(key, value)来快速创建pair
程序中的内存包括静态内存(存储static成员),栈内存(存储非static对象,会自动销毁),堆内存(存储动态分配的对象,即在程序运行时分配的对象,生命周期由程序控制)
动态内存(堆)需要通过new和delete进行申请和释放
shared_ptr允许多个指针指向一个对象,unique_ptr独占对象
shared_ptr<int> p = make_shared(42); //创建一个指向42的指针
auto p2 = make_shared(10, '1');
拷贝构造函数,拷贝赋值运算符
// Foo中有一个string指针ps和int成员变量i
Foo(const Foo& f) //一般形参为const引用
: ps(*f.ps)
, i(p.i) {}
Foo& operator= (const Foo&); //形参为const引用,返回引用(*this)
Foo& Foo::operator= (const Foo&rhs) {
auto newp = new string(*rhs.ps); //分配新地址
delete ps; //释放旧地址
ps = newp; //将数据拷贝到本对象
i = rhs.i;
return *this; //返回本对象
}
Foo(const Foo&) = delete; //阻止拷贝
Foo& operator= (const Foo&) = delete; //阻止赋值
重载输入输出运算符(不能是类的成员函数)
ostream& operator<<(ostream& os, const Foo& item) {
os << item.isbn() << " " << item.name();
return os;
}
istream& operator>>(istream& is, Foo& item) {
is >> item.bookNo >> item.name;
return is;
}
算数运算符形参一般是常量引用
Foo operator+(const Foo& lhs, const Foo& rhs) { //非成员函数
Foo sum = lhs;
sum += rhs; //也需要重新定义+=运算符
return sum;
}
Foo& Foo::operator+=(const Foo& rhs) { //成员函数
a += rhs.a;
return *this;
}
bool operator==(const Foo& lhs, const Foo& rhs) { //非成员函数
return lhs.a() == rhs.a() && lhs.b() == rhs.b();
}
string& Foo::operator[](size_t n) { return vec[n]; } //成员函数,同时定义常量和非常量类型
const string& Foo::operator[](size_t n) { return vec[n]; }
Foo& Foo::operator++() { ++a; return *this; } //前置++,成员函数,返回新值
Foo Foo::operator++(int) { Foo tmp = *this; ++*this; return tmp; } //后置++,成员函数,int形参,内部使用前置++实现,返回原值
基类的析构函数一般定义为虚函数,虚函数可能会在运行时才被解析
final关键字表示该类不能被继承,或是某函数不能被覆盖
模板,函数模板可以推断类型,但类模板必须显式指出类型
template <typename T> T foo(T* p) {
T tmp = *p;
// ...
return tmp;
}
int i = 42;
int* p = &i;
i = foo(p);
template <typename T, typename U>
template <unsigned U> // 非类型参数模板
template <typename T> class Blob {} // 成员函数前也要加template关键字
Blob<int> prices;
explicit关键字用来防止单参数构造函数定义的隐式转换