如何自己做网站 开直播,wordpress获取标签链接,深圳设计网站费用,肯德基网站建设方案1、const
1#xff09;、常量
const一般的用法就是修饰变量、引用、指针#xff0c;修饰之后它们就变成了常量#xff0c;需要注意的是const并未区分出编译期常量和运行期常量#xff0c;并且const只保证了运行时不直接被修改
一般的情况#xff0c;const放在左边…1、const
1、常量
const一般的用法就是修饰变量、引用、指针修饰之后它们就变成了常量需要注意的是const并未区分出编译期常量和运行期常量并且const只保证了运行时不直接被修改
一般的情况const放在左边表示常量
const int x 100; // 常量
const int rx x; // 常量引用
const int* px x; // 常量指针给变量加上const之后变量就成了常量只能读、禁止写编译器会帮你检查出所有对它的写操作发出警告在编译阶段防止有意或者无意的修改。这样一来const常量用起来就相对安全一点。所以在设计函数的时候将参数用const修饰的话一个是可以保证效率另一个是保证安全
2、修饰成员函数
除此之外const还能声明在成员函数上const被放在了函数的后面表示这个函数是一个常量函数的执行过程是const的不会修改对象的状态即成员变量比如
class DemoClass final {
private:const long MAX_SIZE 256; // const成员变量int m_value; // 成员变量
public:int get_value() const // const成员函数{// error: Cannot assign to non-static data member within const member function get_valuem_value 100;return m_value;}
};3、指针常量
const放在*的右边表示指针是常量const pointer to int指针不能被修改而指向的变量可以被修改 int x 100;int b 150;int *const px x;*px 102; // successpx b; // error: Cannot assign to variable px with const-qualified type int *constint *const px和const int* px的区别
int *const px定义了一个指针常量指针本身的地址不可改变但可以改变指针所指向的数据const int* px定义了一个指向常量的指针可以改变指针的地址使其指向其他int但不能通过此指针修改所指向的int值
4、小结
const非const对象实例const T对象只读只能调用const成员函数可以修改对象调用任意成员函数引用const T引用的对象只读只能调用const成员函数指针*const指针指向的对象只读只能调用const成员函数成员函数func() const不允许修改成员变量可以修改成员变量指针常量const T*表示指针是常量指针不能被修改而其指向的变量可以被修改指针和其指向的变量都可以被修改
2、智能指针
1、unique_ptr
unique_ptr是一种独占资源所有权的指针它会自动管理初始化时的指针在离开作用域时析构释放内存
#include iostream
#include memoryint main() {std::unique_ptrint ptr1(new int(10)); // int智能指针assert(*ptr1 10); // 使用*取内容assert(ptr1 ! nullptr); // 判断是否为空指针std::unique_ptrstd::string ptr2(new std::string(hello)); // string智能指针assert(*ptr2 hello); // 使用*取内容assert(ptr2-size() 5); // 使用-调用成员函数return 0;
}在C14的时候新加入了make_unique()函数可以利用它构造一个unique_ptr对象
#include iostream
#include memoryint main() {auto ptr1 std::make_uniqueint(42); // 工厂函数创建智能指针assert(ptr1 *ptr1 42);auto ptr2 std::make_uniquestd::string(god of war); // 工厂函数创建智能指针assert(!ptr2-empty());return 0;
}unique_ptr的所有权
unique_ptr表示指针的所有权是唯一的不允许共享任何时候只能有一个人持有它
为了实现这个目的unique_ptr应用了C的转移move语义同时禁止了拷贝赋值所以在向另一个unique_ptr赋值的时候要特别留意必须用std::move()函数显式地声明所有权转移
赋值操作之后指针的所有权就被转走了原来的unique_ptr变成了空指针新的unique_ptr接替了管理权保证所有权的唯一性
#include iostream
#include memoryint main() {auto ptr1 std::make_uniqueint(42); // 工厂函数创建智能指针assert(ptr1 *ptr1 42); // 此时智能指针有效auto ptr2 std::move(ptr1); // 使用move()转移所有权assert(!ptr1 ptr2); // ptr1变成了空指针return 0;
}unique_ptr作为参数和返回值
unique_ptr作为参数传递不会发生拷贝但是会将对象所有权会转移到函数里如下ptr会在main()方法结束之前被销毁
#include iostream
#include memoryclass Resource {
public:Resource() { std::cout Resource acquired\n; }~Resource() { std::cout Resource destroyed\n; }friend std::ostream operator(std::ostream out, const Resource res) {out I am a resource;return out;}
};void takeOwnership(std::unique_ptrResource res) {if (res) {std::cout *res \n;}
} // Resource对象会在这里销毁int main() {auto ptr{std::make_uniqueResource()};// takeOwnership(ptr); // 不能这样写,unique_ptr禁止了拷贝赋值,需要使用std::move()函数显式地声明所有权转移takeOwnership(std::move(ptr));std::cout Ending program\n;return 0;
}输出
Resource acquired
I am a resource
Resource destroyed
Ending program有时候不想对象的所有权转移到函数里这时候可以通过get()方法获取对象如下
#include iostream
#include memoryclass Resource {
public:Resource() { std::cout Resource acquired\n; }~Resource() { std::cout Resource destroyed\n; }friend std::ostream operator(std::ostream out, const Resource res) {out I am a resource;return out;}
};void takeOwnership(Resource *res) {if (res) {std::cout *res \n;} else {std::cout No resource\n;}
}int main() {auto ptr{std::make_uniqueResource()};takeOwnership(ptr.get());std::cout Ending program\n;return 0;
} // Resource对象会在这里销毁输出
Resource acquired
I am a resource
Ending program
Resource destroyedunique_ptr可以直接作为返回值返回
#include iostream
#include memoryclass Resource {
public:Resource() { std::cout Resource acquired\n; }~Resource() { std::cout Resource destroyed\n; }friend std::ostream operator(std::ostream out, const Resource res) {out I am a resource;return out;}
};std::unique_ptrResource createResource() {return std::make_uniqueResource();
}int main() {auto ptr{createResource()};return 0;
}在C14及之前的版本中会使用std::move来返回Resource对象在C17及以后版本中进行了RVO优化https://en.wikipedia.org/wiki/Copy_elision尽管没有显式使用std::move编译器依然能够识别并优化这个返回过程直接将新创建的Resource对象的所有权从createResource函数内部转移到了main函数中的ptr变量而无需实际执行移动构造函数
2、shared_ptr
shared_ptr和unique_ptr不同是它的所有权是可以被安全共享的也就是说支持拷贝赋值允许被多个人同时持有就像原始指针一样
在底层实现中shared_ptr采用引用计数的方式实现。引用计数最开始的时候是1表示只有一个持有者。如果发生拷贝赋值——也就是共享的时候引用计数就增加为了保证并发安全引用计数器的加1减1操作都是原子操作而发生析构销毁的时候引用计数就减少。只有当引用计数减少到0也就是说没有任何人使用这个指针的时候它才会真正调用delete释放内存
#include iostream
#include memoryclass Resource {
public:Resource() { std::cout Resource acquired\n; }~Resource() { std::cout Resource destroyed\n; }
};int main() {auto ptr1 std::make_sharedResource(); // 工厂函数创建智能指针assert(ptr1 ptr1.unique()); // 此时智能指针有效且唯一{auto ptr2 ptr1; // 直接拷贝赋值,不需要使用move()assert(ptr1 ptr2); // 此时两个智能指针均有效assert(ptr1 ptr2); // shared_ptr可以直接比较// 两个智能指针均不唯一,且引用计数为2assert(!ptr1.unique() ptr1.use_count() 2);assert(!ptr2.unique() ptr2.use_count() 2);std::cout Killing one shared pointer\n;} // ptr2离开了作用域,但是没有资源被销毁assert(ptr1 ptr1.unique()); // 此时智能指针有效且唯一std::cout Killing another shared pointer\n;return 0;
} // ptr1离开了作用域,Resource对象会在这里销毁输出
Resource acquired
Killing one shared pointer
Killing another shared pointer
Resource destroyed在上面的例子中 ptr2在自己的作用域中被创建然后出了作用域后ptr2虽然被销毁但是所管理的资源却在main方法结束后才被销毁
enable_shared_from_this
如果不小心直接在类里面返回this对象想要获得该对象的shared_ptr那么会让一个对象被delete两次如下
#include iostream
#include memoryclass Resource {
public:Resource() { std::cout Resource acquired\n; }~Resource() { std::cout Resource destroyed\n; }std::shared_ptrResource GetSPtr() {return std::shared_ptrResource(this);}
};int main() {auto sptr1 std::make_sharedResource();auto sptr2 sptr1-GetSPtr();return 0;
}输出
Resource acquired
Resource destroyed
studyProject(6959,0x7ff854f04700) malloc: *** error for object 0x6000035ed1f8: pointer being freed was not allocated
studyProject(6959,0x7ff854f04700) malloc: *** set a breakpoint in malloc_error_break to debug上面的代码其实会生成两个独立的shared_ptr他们的控制块是独立的所以导致Resource被释放了两次 使用enable_shared_from_this可以避免上述情况
#include iostream
#include memoryclass Resource : public std::enable_shared_from_thisResource {
public:Resource() { std::cout Resource acquired\n; }~Resource() { std::cout Resource destroyed\n; }std::shared_ptrResource GetSPtr() {return shared_from_this();}
};int main() {auto sptr1 std::make_sharedResource();auto sptr2 sptr1-GetSPtr();return 0;
}shared_ptr的循环引用问题
shared_ptr的引用计数也导致了一个新的问题就是循环引用这在把shared_ptr作为类成员的时候最容易出现典型的例子就是链表节点
#include iostream
#include memoryclass Node {
public:using this_type Node;using shared_type std::shared_ptrthis_type;Node(const std::string name) : name(name) {std::cout name created\n;}~Node() {std::cout name destroyed\n;}std::string name;shared_type next; // 使用智能指针来指向下一个节点
};int main() {auto n1 std::make_sharedNode(n1);auto n2 std::make_sharedNode(n2);assert(n1.use_count() 1); // 引用计数为1assert(n2.use_count() 1);n1-next n2; // 两个节点互指,形成了循环引用n2-next n1;assert(n1.use_count() 2); // 引用计数为2assert(n2.use_count() 2); // 无法减到0,无法销毁,导致内存泄漏return 0;
}输出
n1 created
n2 created上面的代码中两个节点指针刚创建时引用计数是1但指针互指即拷贝赋值之后引用计数都变成了2
这个时候shared_ptr意识不到这是一个循环引用多算了一次计数后果就是引用计数无法减到0无法调用析构函数执行delete最终导致内存泄漏
3、weak_ptr
weak_ptr是专门为打破循环引用而设计它实际上不会托管对象它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期也就是将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。在需要的时候可以调用weak_ptr的成员函数lock()获取shared_ptr强引用
#include iostream
#include memoryclass Node {
public:using this_type Node;using shared_type std::weak_ptrthis_type; // 注意这里,别名改用weak_ptrNode(const std::string name) : name(name) {std::cout name created\n;}~Node() {std::cout name destroyed\n;}std::string name;shared_type next;
};int main() {auto n1 std::make_sharedNode(n1);auto n2 std::make_sharedNode(n2);n1-next n2; // 两个节点互指,形成了循环引用n2-next n1;assert(n1.use_count() 1); // 因为使用了weak_ptr,引用计数为1assert(n2.use_count() 1); // 打破循环引用,不会导致内存泄漏if (!n1-next.expired()) { // 检查指针是否有效auto ptr n1-next.lock(); // lock()获取shared_ptrassert(ptr n2);}return 0;
}输出
n1 created
n2 created
n2 destroyed
n1 destroyed4、小结
unique_ptr是一种独占资源所有权的指针它会在栈上分配然后在离开作用域之后进行释放删除里面持有的对象它只能使用move语义转移对象。所以如果你想操作一个指针在进入作用域的时候分配好内存然后在离开作用域的时候安全释放对象那么可以使用它
shared_ptr所管理的资源可以被多个对象持有并且使用引用计数策略来释放对象如果计数没有清零那么它所管理的资源不会释放
weak_ptr不管理对象只是shared_ptr对象管理的资源的观察者所以它不影响共享资源的生命周期它用于解决shared_ptr循环引用
3、lambda表达式
1、lambda基本使用
#include iostreamint main() {auto func [](int x) // 定义一个lambda表达式{std::cout x * x std::endl; // lambda表达式的具体内容};func(3); // 调用lambda表达式return 0;
}C里的lambda表达式除了可以像普通函数那样被调用还有一个普通函数所不具备的特殊本领就是可以捕获外部变量在内部的代码里直接操作
#include iostreamint main() {int n 10; // 一个外部变量auto func [](int x) // lambda表达式用捕获{std::cout x * n std::endl; // 直接操作外部变量};func(3); // 调用lambda表达式return 0;
}2、使用lambda的注意事项
1lambda的形式
嵌套定义lambda表达式
#include iostreamint main() {auto f1 []() // 定义一个lambda表达式{std::cout lambda f1 std::endl;auto f2 [](int x) // 嵌套定义lambda表达式{return x * x;}; // lambda f2std::cout f2(10) std::endl;}; // lambda f1f1();return 0;
}匿名lambda表达式
#include iostreamint main() {std::vectorint v {3, 1, 8, 5, 0}; // 标准容器std::cout *find_if(begin(v), end(v), // 标准库里的查找算法[](int x) // 匿名lambda表达式,不需要auto赋值{return x 5; // 用做算法的谓词判断条件}) std::endl; // 语句执行完,lambda表达式就不存在了return 0;
}2lambda的变量捕获
lambda的变量捕获要点
[]表示按值捕获所有外部变量表达式内部是值的拷贝并且不能修改[]按引用捕获所有外部变量内部以引用的方式使用可以修改可以在[]里明确写出外部变量名指定按值或者按引用捕获
int main() {int x 33; // 一个外部变量auto f1 []() // lambda表达式,用按值捕获{//x 10; // x只读,不允许修改};auto f2 []() // lambda表达式,用按引用捕获{x 10; // x是引用,可以修改};auto f3 [, x]() // lambda表达式,用按引用捕获x,其他的按值捕获{x 20; // x是引用,可以修改};return 0;
}3泛型的lambda
在C14里lambda表达式可以实现泛型化相当于简化了的模板函数
#include iostreamint main() {auto f [](const auto x) // 参数使用auto声明,泛型化{return x x;};std::cout f(3) std::endl; // 参数类型是intstd::cout f(0.618) std::endl; // 参数类型是doublestd::string str matrix;std::cout f(str) std::endl; // 参数类型是stringreturn 0;
}3、小结
lambda表达式是一个闭包能够像函数一样被调用像变量一样被传递可以使用auto自动推导类型存储lambda表达式但C鼓励尽量就地匿名使用缩小作用域lambda表达式使用[]的方式按值捕获使用[]的方式按引用捕获空的[]则是无捕获也就相当于普通函数捕获引用时必须要注意外部变量的生命周期防止变量失效C14里可以使用泛型的lambda表达式相当于简化的模板函数
参考
07 | const/volatile/mutable常量/变量究竟是怎么回事
C 中让人头晕的const constexpr
08 | smart_ptr智能指针到底“智能”在哪里
写给[C ]新人智能指针避坑指南
10 | lambda函数式编程带来了什么