动态内存
- unique_ptr is a smart pointer which owns an object exclusively. shared_ptr is a smart pointer for shared ownership. It is both copyable and movable.
- 结合 copy constructor 和 assignment copy operator 来理解,
- https://stackoverflow.com/questions/6876751/differences-between-unique-ptr-and-shared-ptr
- 两种初始化的方法:
auto g = make_shared<int>(42); auto x = shared_ptr<int>(new int(1024)); - new 的一些细节
auto x = new auto(obj); // auto, 利用 obj 的类型初始化 x
int * p = new arrT; // 利用 arrT 的类型初始化 p
const string * s = new const string; // const object
int * p2 = new(nothrow) int; // placement new
-
不能直接使用 weak_ptr 来访问数据,而是要首先调用 lock 进行检查
-
使用标准库的容器而不是 array
-
因为动态数组并不是数组类型,所以不能使用迭代器 begin 和 end 处理, 也不可以使用 范围for
-
初始化动态分配的数组:
- 通过在数组后面加上一个空括号可以对于元素进行 值初始化
- 使用 元素初始化器的花括号列表 进行初始化
class A {
public:
A(int x, int y) {
cout << x << "-- " << y << endl;
}
};
int main(int argc, char *argv[]) {
A * a = new A[10](1, 2); // 错误,这不是值初始化
int * b = new int[10]; // 10 个没有初始化的 int
int * c = new int[10](); // 10 个 0
string * d = new string[10]; // 10 个空 string
string * e = new string[10](); // 10 个空 string
return 0;
}
class A {
public:
A(int x, int y) { cout << x << "-- " << y << endl; }
A(int x) { cout << x << endl; }
A() { cout << "default" << endl; }
};
int main(int argc, char *argv[]) {
auto a = new A[10]{1, 2, 3};
return 0;
}
1
2
3
default
default
default
default
default
default
default
-
在书中,按照 c++ 11 的标准,shared_ptr 和 array 不能融洽的工作,但是 c++17 之后,这个问题解决了
-
12 章现在只是剩下 12.3 没有看了
lambda
lambda 的唯一挑战应该就在于数值捕获吧 可能你还不太会使用 Lambda,基本形式如下:
[](argument1,argument2,.....)-> return_type {//code}
在 () 中传入参数,在 {} 中编写代码,[] 是一个捕获列表,可以指定外部作用域中,可以使用的局部变量:
- [] — 捕获列表为空,表示在 lambda 表达式中不能使用任何外部作用域的局部变量,只能使用传进去的参数。
- [=] — 表示按照值传递的方法捕获父作用域的所有变量。
- [&] — 表示按照引用传递的方法捕获父作用域的所有变量。
- [this] — 在成员函数中,按照值传递的方法捕获 this 指针。
- [a, &b] — 不建议直接使用 [=] 或 [&] 捕获所有变量,可以按需显式捕获,就是在 [] 指定变量名,[a] 表示值传递,[&b] 表示引用传递。
- 可以忽略参数和返回值, 比如
auto f = [] { return 42; }; -
一个 lambda 表达式只能使用那些明确指明的局部变量变量
- lambda 表达式在创建的时候,其实是在创建一个隐藏的对象,并且该对象定义了一个 () 操作符
- 之所以 lambda 需要对于局部变量进行 explicit 的捕获,是因为 lambda 再次被使用的时候,这些变量可能都不存在了
- 当然捕获局部变量的 reference 需要自己注意
- https://stackoverflow.com/questions/24474957/why-lambda-captures-only-automatic-storage-variables
operator
除了函数调用 operator() 之外,其他的重载运算符不能含有默认实参
-
new 和 delete 还可以重载
-
是函数成员和不是的情况例子
因为 string 将 + 定义为非成员函数,所以 “hi” + s 等价于 operator+("hi", s);
string s="fadf";
s = operator+("hi", s);
输入输出运算符必须是非成员函数,如果想要输出非 public 成员,还需要将 IO 类声明为 friend
为什么 = [] -> 必须是 member function
- 函数对象通常是泛型算法实参.
箭头运算符的限定
- 574 中关于 lambda 表达式有点看不懂
使用 function 来解决不同类型具有相同的调用形式。
- 14.9.2 section 中间关于 转换的二义性 的描述,例子还是很容易理解,但是术语很麻烦。
面向对象程序设计
15.7
-
因为定义了析构函数,而导致移动函数被删除 ?
- copy constructor with inheritence
- 是不是说,当 base 的 copy constructor 是 private 的时候(被删除了), 导致所有的 derived 都是不可以被访问了
- 如何调用 parent 的 constructor ?
-
被删除指的是编译器拒绝为其生成该函数,因为即便是生成了之后,导致语言错误
- P476 : 与拷贝操作不同,移动操作永远被隐式定义为删除的函数
class A {
public:
A(const A &a) { cout << "A copy" << endl; }
A(int x) : _x(x) {}
A() : _x(100) {}
int get() { return _x; }
private:
int _x;
};
class B : public A {
public:
B(const B &b) : A(b) { cout << "B copy" << endl; }
B(B &&b) : A(std::move(b)) { cout << "B move" << endl; }
// B(B &&b) { cout << "B move" << endl; } // 这会导致 base 部分没有 move 而是默认初始化
B(int x) : A(x) {}
};
int main(int argc, char *argv[]) {
B a(12);
B b(a);
vector<B> v;
v.push_back(std::move(b));
cout << v[0].get();
return 0;
}
- 为什么需要
A() = default;, 如果编译器合成了就合成了,根本不需要我们 explicit 的写出来啊 !- 因为定义了构造函数之后,默认构造函数就不会被 implict 的创建了
- 一个类只初始化其直接基类,所以只能继承直接基类的构造函数
- 实现继承的方式是利用 using 语句
- 默认,拷贝,移动构造函数不会被继承
- 如果一个类只是含有继承的构造函数,那么这个类将会一个合成默认构造函数
template
- 模板参数列表中间,typename 和 class 是相同的
- 类型参数前必须使用发关键字 class 或者 typename
- 除了类型参数,还可以定义非类型参数,非类型参数必须被常量表达式初始化
- 绑定到指针或者引用非类型参数的实参必须具有静态的生存期
- inline 和 constexpr 放到模板参数列表之后
- 函数模板和类模板函数定义通常放在头文件中间
- 与函数模板不同,编译器不能为类模板推断模板参数类型
- 和 friend 的关系取决于声明的方式,可以让所有的类型成为 friend, 也可以只是特定的类型,当然也可以是对应的类型
- 利用 using 可以提前固定 template 的参数
- 和普通的 static 成员类似,template 的静态成员也需要有且仅有一个定义
using namespace std;
template <typename T, typename U> class A {
public:
T a;
static T x;
};
template <typename T, typename U> T A<T, U>::x = 1;
int main(int argc, char *argv[]) {
A<int, int> x;
A<int, int> x1;
A<int, double> y;
x.x = 12;
cout << x.x << endl;
cout << x1.x << endl;
cout << y.x << endl;
return 0;
}
-
使用
::可以访问类型成员和静态成员,所以为了说明这是类型成员,需要使用关键字 typename -
模板参数也可以提供默认参数,如果每一个参数都有默认参数,类模板的实例化也不可以去掉
<> -
模板类继续定义 template 成员函数 ```cpp using namespace std;
template <typename T, typename U> class A {
public:
T a;
template
template <typename T, typename U> template
int main(int argc, char *argv[]) {
A<int, int> a;
a.compare
return 0; }
- 对于实例化声明,必须存在一个 explicit 的实例化定义
- [ ] 16.1.6 中间描述关于 unique_ptr 和 shared_ptr 的内容,比较迷惑,等到回头理解了动态内存分配相关的内容再说吧
- 如果形参是 reference ,那么数组不会被转换为指针, 下面的代码正确,但是数组 a b 的长度应该保证。
```cpp
template <typename T> void gg(T &a, T &b) {
cout << "array reference" << endl;
}
int main(int argc, char *argv[]) {
int a[12];
int b[12];
gg(a, b);
return 0;
}
-
第一个模板实参和第一个模板参数匹配,依次类推,之后后面的部分才可以用于推导
- 16.2.5 到底在分析什么
- 如果 template 函数的参数是 T && 类型,在进行参数类型推到的时候
- 引用折叠
-
后面的两个 section 16.2.6 和 16.2.7 也是基于此的,重新看看应该不会很难吧!
-
615 的 char * 和 « 的关系莫名其妙
- 缺少声明可能导致程序行为异常 P700
- 16.3 又是各种类型匹配,脑壳痛
用于大型程序的工具
-
如果继承多个基类的构造函数,并且其中存在构造函数的参数列表完全相同,那么必须定义自己的构造函数阻止二义性
-
编译器不会在派生类向基类的几种转换中进行比较和选择,在它看来都是一样好,所以如果出现多种选择,那么就会触发二义性错误
-
与只有基类的对象相同,对象,指针和引用的静态类型决定了我们能够使用那些成员。
-
多重继承导致类作用域的搜索可以同时在多个基类中间进行,因此会出现二义性错误,可以显示的指出,但是最好还是重新定义一次
class A {
private:
void get() { cout << "c" << endl; }
};
class B {
public:
void get() { cout << "c" << endl; }
};
class C : public A, public B {};
int main(int argc, char *argv[]) {
C c;
c.B::get();
return 0;
}
- 虚基类总是先于非虚基类初始化,如果一个类中间存在多个虚基类,那么按照在派生列表中间出现的顺序决定。
本站所有文章转发 CSDN 将按侵权追究法律责任,其它情况随意。