This commit is contained in:
Light-City
2020-03-03 11:13:16 +08:00
parent 16c12a3bc6
commit 1dca8dee82
84 changed files with 25 additions and 25 deletions

Binary file not shown.

View File

@@ -0,0 +1,68 @@
cmake_minimum_required(VERSION 3.14)
project(Morden_C++)
set(CMAKE_CXX_STANDARD 17)
add_executable(heap RAII/heap.cpp)
add_executable(stack RAII/stack.cpp)
add_executable(RAII RAII/RAII.cpp)
add_executable(RAII_fstram RAII/RAII_fstram.cpp)
add_executable(auto_scope smart_ptr/auto_scope.cpp)
add_executable(unique_ptr smart_ptr/unique_ptr.cpp)
add_executable(unique_ptr_U smart_ptr/unique_ptr_U.cpp)
add_executable(shared_ptr smart_ptr/shared_ptr.cpp)
add_executable(reference reference/reference.cpp)
add_executable(forward reference/forward.cpp)
add_executable(collapses reference/collapses.cpp)
add_executable(lifetime reference/lifetime.cpp)
add_executable(dontreturnReference reference/don'treturnReference.cpp)
add_executable(container container1/container.cpp)
add_executable(cont container2/hash.cpp)
add_executable(vector_l container1/vector_l.cpp)
add_executable(priority_queue container2/priority_queue.cpp)
add_executable(relacontainer container2/relacontainer.cpp)
add_executable(unorder container2/unorder.cpp)
add_executable(array container2/array.cpp)
add_executable(exception exception/exception.cpp)
add_executable(returnObj returnObj/returnObj1.cpp)
add_executable(returnObj_add returnObj/returnObj2.cpp)
add_executable(returnObj3 returnObj/returnObj3.cpp)
add_executable(returnObj4 returnObj/returnObj4.cpp)
add_executable(returnObj5 returnObj/returnObj5.cpp)
add_executable(rvonrvo returnObj/all.cpp)
add_executable(literal literalAssert/literal.cpp)
add_executable(assert literalAssert/assert.cpp)
add_executable(default_delete literalAssert/default_delete.cpp)
add_executable(overridefinal literalAssert/overridefinal.cpp)
add_executable(SFINAE "SFINAE/SFINAE.cpp")
add_executable(serialize "SFINAE/sfinae paper/serialize.cpp")
add_executable(hana "SFINAE/sfinae paper/hana.cpp")
add_executable(overload "SFINAE/sfinae paper/overload1.cpp")
add_executable(pSFINAE "SFINAE/sfinae paper/p1SFINAE.cpp")
add_executable(p1SFINAE "SFINAE/sfinae paper/p2SFINAE.cpp")
add_executable(sizeof1 "SFINAE/sfinae paper/sizeof1.cpp")
add_executable(sizeof2 "SFINAE/sfinae paper/sizeof2.cpp")
add_executable(combining "SFINAE/sfinae paper/combiningAndGenius.cpp")
add_executable(overload2 "SFINAE/sfinae paper/overload2.cpp")
add_executable(combining1 "SFINAE/sfinae paper/timeGenius.cpp")
add_executable(decltype "SFINAE/sfinae paper/decltype.cpp")
add_executable(auto "SFINAE/sfinae paper/auto.cpp")
add_executable(constexpr "SFINAE/sfinae paper/constexpr.cpp")
add_executable(blending "SFINAE/sfinae paper/blending1.cpp")
add_executable(blending2 "SFINAE/sfinae paper/blending2.cpp")
add_executable(lambda "SFINAE/sfinae paper/lambda.cpp")
add_executable(is_valid "SFINAE/sfinae paper/is_valid.cpp")
add_executable(fis_valid "SFINAE/sfinae paper/fis_valid.cpp")
add_executable(packis_valid "SFINAE/sfinae paper/packis_valid.cpp")
add_executable(test3 constexpr/test3.cpp)
add_executable(sqrt constexpr/sqrt.cpp)

View File

@@ -0,0 +1,64 @@
cmake_minimum_required(VERSION 3.14)
project(Morden_C++)
set(CMAKE_CXX_STANDARD 17)
add_executable(heap RAII/heap.cpp)
add_executable(stack RAII/stack.cpp)
add_executable(RAII RAII/RAII.cpp)
add_executable(RAII_fstram RAII/RAII_fstram.cpp)
add_executable(auto_scope smart_ptr/auto_scope.cpp)
add_executable(unique_ptr smart_ptr/unique_ptr.cpp)
add_executable(unique_ptr_U smart_ptr/unique_ptr_U.cpp)
add_executable(shared_ptr smart_ptr/shared_ptr.cpp)
add_executable(reference reference/reference.cpp)
add_executable(forward reference/forward.cpp)
add_executable(collapses reference/collapses.cpp)
add_executable(lifetime reference/lifetime.cpp)
add_executable(dontreturnReference reference/don'treturnReference.cpp)
add_executable(container container1/container.cpp)
add_executable(cont container2/hash.cpp)
add_executable(vector_l container1/vector_l.cpp)
add_executable(priority_queue container2/priority_queue.cpp)
add_executable(relacontainer container2/relacontainer.cpp)
add_executable(unorder container2/unorder.cpp)
add_executable(array container2/array.cpp)
add_executable(exception exception/exception.cpp)
add_executable(returnObj returnObj/returnObj1.cpp)
add_executable(returnObj_add returnObj/returnObj2.cpp)
add_executable(returnObj3 returnObj/returnObj3.cpp)
add_executable(returnObj4 returnObj/returnObj4.cpp)
add_executable(returnObj5 returnObj/returnObj5.cpp)
add_executable(rvonrvo returnObj/all.cpp)
add_executable(literal literalAssert/literal.cpp)
add_executable(assert literalAssert/assert.cpp)
add_executable(default_delete literalAssert/default_delete.cpp)
add_executable(overridefinal literalAssert/overridefinal.cpp)
add_executable(SFINAE "SFINAE/SFINAE.cpp")
add_executable(serialize "SFINAE/sfinae paper/serialize.cpp")
add_executable(hana "SFINAE/sfinae paper/hana.cpp")
add_executable(overload "SFINAE/sfinae paper/overload1.cpp")
add_executable(pSFINAE "SFINAE/sfinae paper/p1SFINAE.cpp")
add_executable(p1SFINAE "SFINAE/sfinae paper/p2SFINAE.cpp")
add_executable(sizeof1 "SFINAE/sfinae paper/sizeof1.cpp")
add_executable(sizeof2 "SFINAE/sfinae paper/sizeof2.cpp")
add_executable(combining "SFINAE/sfinae paper/combiningAndGenius.cpp")
add_executable(overload2 "SFINAE/sfinae paper/overload2.cpp")
add_executable(combining1 "SFINAE/sfinae paper/timeGenius.cpp")
add_executable(decltype "SFINAE/sfinae paper/decltype.cpp")
add_executable(auto "SFINAE/sfinae paper/auto.cpp")
add_executable(constexpr "SFINAE/sfinae paper/constexpr.cpp")
add_executable(blending "SFINAE/sfinae paper/blending1.cpp")
add_executable(blending2 "SFINAE/sfinae paper/blending2.cpp")
add_executable(lambda "SFINAE/sfinae paper/lambda.cpp")
add_executable(is_valid "SFINAE/sfinae paper/is_valid.cpp")
add_executable(fis_valid "SFINAE/sfinae paper/fis_valid.cpp")
add_executable(packis_valid "SFINAE/sfinae paper/packis_valid.cpp")

View File

@@ -0,0 +1,96 @@
#include <iostream>
#include <mutex>
#include <fstream>
using namespace std;
enum class shape_type {
circle,
triangle,
rectangle,
};
class shape {
public:
shape() { cout << "shape" << endl; }
virtual void print() {
cout << "I am shape" << endl;
}
virtual ~shape() {}
};
class circle : public shape {
public:
circle() { cout << "circle" << endl; }
void print() {
cout << "I am circle" << endl;
}
};
class triangle : public shape {
public:
triangle() { cout << "triangle" << endl; }
void print() {
cout << "I am triangle" << endl;
}
};
class rectangle : public shape {
public:
rectangle() { cout << "rectangle" << endl; }
void print() {
cout << "I am rectangle" << endl;
}
};
// 利用多态 上转 如果返回值为shape,会存在对象切片问题。
shape *create_shape(shape_type type) {
switch (type) {
case shape_type::circle:
return new circle();
case shape_type::triangle:
return new triangle();
case shape_type::rectangle:
return new rectangle();
}
}
class shape_wrapper {
public:
explicit shape_wrapper(shape *ptr = nullptr) : ptr_(ptr) {}
~shape_wrapper() {
delete ptr_;
}
shape *get() const {
return ptr_;
}
private:
shape *ptr_;
};
void foo() {
shape_wrapper ptr(create_shape(shape_type::circle));
ptr.get()->print();
}
int main() {
// 第一种方式
shape *sp = create_shape(shape_type::circle);
sp->print();
delete sp;
// 第二种方式
foo();
return 0;
}

View File

@@ -0,0 +1,38 @@
#include <iostream>
using namespace std;
class bar {
};
// java 程序员风格
void foo() {
cout << "method 1" << endl;
bar *ptr = new bar();
delete ptr;
}
bar *make_bar() {
bar *ptr = nullptr;
try {
ptr = new bar();
} catch (...) {
delete ptr;
throw;
}
return ptr;
}
// 独立出函数 分配和释放不在一个函数里
void foo1() {
cout << "method 2" << endl;
bar *ptr = make_bar();
delete ptr;
}
int main() {
foo();
foo1();
return 0;
}

View File

@@ -0,0 +1,26 @@
//
// Created by light on 19-12-9.
//
#include <iostream>
class Obj {
public:
Obj() { puts("Obj()"); }
~Obj() { puts("~Obj()"); }
};
void foo(int n)
{
Obj obj;
if (n == 42)
throw "life, the universe and everything";
}
// 不管是否发生了异常obj 的析构函数都会得到执行。
int main()
{
try {
foo(41);
foo(42);
}
catch (const char* s) {
puts(s);
}
}

View File

@@ -0,0 +1 @@
学习自https://jguegant.github.io/blogs/tech/sfinae-introduction.html

View File

@@ -0,0 +1,41 @@
//
// Created by light on 20-1-5.
//
#include <iostream>
using namespace std;
template<typename T>
class IsClassT {
private:
typedef char One;
typedef struct {
char a[2];
} Two;
template<typename C>
static One test(int C::*);
// Will be chosen if T is anything except a class.
template<typename C>
static Two test(...);
public:
enum {
Yes = sizeof(IsClassT<T>::test<T>(0)) == 1
};
enum {
No = !Yes
};
};
struct A {
};
int main() {
// 0不能转换为int int::*因为int不是类所以它不能有成员指针。
cout << IsClassT<int>::Yes << endl;
cout << IsClassT<A>::Yes << endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,26 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
bool f();
auto test = f(); // Famous usage, auto deduced that test is a boolean, hurray!
// vvv t wasn't declare at that point, it will be after as a parameter!
template<typename T>
decltype(t.serialize()) g(const T &t) {} // Compilation error
// Less famous usage:
// vvv auto delayed the return type specification!
// vvv vvv the return type is specified here and use t!
template<typename T>
auto g(const T &t) -> decltype(t.serialize()) {} // No compilation error.
auto myFunction() // Automagically figures out that myFunction returns ints.
{
return int();
}

View File

@@ -0,0 +1,48 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "structData.h"
template<class T>
struct hasSerialize {
// We test if the type has serialize using decltype and declval.
// decltype将根据逗号表达式选择最后一个类型作为返回值
// 如果C有serialize将调用当前版本,没有serialize,则失败,此时编译时不报错,继续寻找重载函数,也就是SFINAE.
template<typename C>
static constexpr decltype(std::declval<C>().serialize(), bool()) test(int /* unused */) {
// We can return values, thanks to constexpr instead of playing with sizeof.
return true;
}
template<typename C>
static constexpr bool test(...) {
return false;
}
// int is used to give the precedence!
static constexpr bool value = test<T>(int());
};
template<class T>
std::string serialize(const T &obj) {
// 不加constexpr 报错error: no member named 'serialize' in 'A'.
if constexpr (hasSerialize<T>::value)
return obj.serialize();
else
return to_string(obj);
}
int main() {
A a;
B b;
C c;
// The following lines work like a charm!
std::cout << serialize(a) << std::endl;
std::cout << serialize(b) << std::endl;
std::cout << serialize(c) << std::endl;
}

View File

@@ -0,0 +1,37 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "structData.h"
template<typename T, typename=std::string>
struct hasSerialize : std::false_type {
};
template<typename T>
struct hasSerialize<T, decltype(std::declval<T>().serialize())> : std::true_type {
};
template<class T>
std::string serialize(const T &obj) {
// 不加constexpr 报错error: no member named 'serialize' in 'A'.
if constexpr (hasSerialize<T>::value) // c++17特性
return obj.serialize();
else
return to_string(obj);
}
int main() {
A a;
B b;
C c;
// The following lines work like a charm!
std::cout << serialize(a) << std::endl;
std::cout << serialize(b) << std::endl;
std::cout << serialize(c) << std::endl;
}

View File

@@ -0,0 +1,96 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "structData.h"
template<class T>
struct hasSerialize {
// 编译时比较
typedef char yes[1];
typedef char no[2];
// 允许我们检查序列化确实是一种方法
// 第二个参数必须是第一个参数的类型
// 例如: reallyHas<int,10> 替换为 reallyHas<int,int 10> 并起作用
// 注意:它仅适用于整数常量和指针(因此函数指针可以使用)
// 例如reallyHas<std::string (C::*)(), &C::serialize> 替换为
// reallyHas<std::string (C::*)(), std::string (C::*)() &C::serialize> 并起作用
template<typename U, U u>
struct reallyHas;
// std::string (C::*)() 是函数指针声明
template<typename C>
static yes &test(reallyHas<std::string (C::*)(), &C::serialize> * /*unused*/) {}
// ()()const 函数指针 -> std::string serialize() const
template<typename C>
static yes &test(reallyHas<std::string (C::*)() const, &C::serialize> * /*unused*/) {}
// The famous C++ sink-hole.
// Note that sink-hole must be templated too as we are testing test<T>(0).
// If the method serialize isn't available, we will end up in this method.
template<typename>
static no &test(...) { /* dark matter */ }
//用作测试的返回值的常数。
//由于编译时评估的大小,因此实际上在这里完成了测试。
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
// Using the previous A struct and hasSerialize helper.
struct D : A {
std::string serialize() const {
return "I am a D!";
}
};
template<class T>
bool testHasSerialize(const T & /*t*/) { return hasSerialize<T>::value; }
// 不能够判断仿函数里面的serialize
struct E {
struct Functor {
std::string operator()() {
return "I am a E!";
}
};
Functor serialize;
};
template<class T>
std::string serialize(const T &obj) {
// 不加constexpr 报错error: no member named 'serialize' in 'A'.
if constexpr (hasSerialize<T>::value)
return obj.serialize();
else
return to_string(obj);
}
int main() {
// 检测结构体是否有serialize方法
// Using the struct A, B, C defined in the previous hasSerialize example.
std::cout << hasSerialize<A>::value << std::endl;
std::cout << hasSerialize<B>::value << std::endl;
std::cout << hasSerialize<C>::value << std::endl;
D d;
A &a = d; // Here we lost the type of d at compile time.
std::cout << testHasSerialize(d) << std::endl; // Output 1.
std::cout << testHasSerialize(a) << std::endl; // Output 0.
E e;
std::cout << e.serialize() << std::endl; // Succefully call the functor.
std::cout << testHasSerialize(e) << std::endl; // Output 0.
A a_;
B b_;
C c_;
std::cout << serialize(a_) << std::endl;
std::cout << serialize(b_) << std::endl;
std::cout << serialize(c_) << std::endl;
}

View File

@@ -0,0 +1,25 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
constexpr int factorial(int n) {
return n <= 1 ? 1 : (n * factorial(n - 1));
}
struct testStruct : std::true_type {
}; // Inherit from the true type.
int main() {
int i = factorial(5); // Call to a constexpr function.
// Will be replace by a good compiler by:
// int i = 120;
std::cout << i << std::endl;
constexpr bool testVar = testStruct(); // Generate a compile-time testStruct.
bool test = testStruct::value; // Equivalent to: test = true;
std::cout << test << std::endl;
test = testVar; // true_type has a constexpr converter operator, equivalent to: test = true;
std::cout << test << std::endl;
}

View File

@@ -0,0 +1,20 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
struct Default {
int foo() const {return 1;}
};
struct NonDefault {
NonDefault(const NonDefault&) {}
int foo() const {return 1;}
};
int main()
{
decltype(Default().foo()) n1 = 1; // int n1
// decltype(NonDefault().foo()) n2 = n1; // error: no default constructor
decltype(std::declval<NonDefault>().foo()) n2 = n1; // int n2
std::cout << "n2 = " << n2 << '\n';
}

View File

@@ -0,0 +1,78 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "boost/hana.hpp"
#include "structData.h"
template<typename UnnamedType>
struct container {
// Let's put the test in private.
private:
// We use std::declval to 'recreate' an object of 'UnnamedType'.
// We use std::declval to also 'recreate' an object of type 'Param'.
// We can use both of these recreated objects to test the validity!
template<typename Param>
constexpr auto testValidity(int /* unused */)
-> decltype(std::declval<UnnamedType>()(std::declval<Param>()),boost::hana::true_c) {
// If substitution didn't fail, we can return a true_type.
return boost::hana::true_c;
}
template<typename Param>
constexpr std::false_type testValidity(...) {
// Our sink-hole returns a false_type.
return boost::hana::false_c;
}
public:
// A public operator() that accept the argument we wish to test onto the UnnamedType.
// Notice that the return type is automatic!
template<typename Param>
constexpr auto operator()(const Param &p) {
// The argument is forwarded to one of the two overloads.
// The SFINAE on the 'true_type' will come into play to dispatch.
// Once again, we use the int for the precedence.
return testValidity<Param>(int());
}
};
template<typename UnnamedType>
constexpr auto is_valid(const UnnamedType &t) {
// We used auto for the return type: it will be deduced here.
return container<UnnamedType>();
}
// Check if a type has a serialize method.
auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {});
// Notice how I simply swapped the return type on the right?
template<class T>
auto serialize(T &obj)
-> typename std::enable_if<decltype(hasSerialize(obj))::value, std::string>::type {
return obj.serialize();
}
template<class T>
auto serialize(T &obj)
-> typename std::enable_if<!decltype(hasSerialize(obj))::value, std::string>::type {
return to_string(obj);
}
int main() {
A a;
B b;
C c;
auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {});
std::cout << test(a) << std::endl;
std::cout << test(b) << std::endl;
std::cout << test(c) << std::endl;
// The following lines work like a charm!
std::cout << serialize(a) << std::endl;
std::cout << serialize(b) << std::endl;
std::cout << serialize(c) << std::endl;
}

View File

@@ -0,0 +1,28 @@
//
// Created by light on 20-1-6.
//
// https://www.boost.org/doc/libs/1_62_0/libs/hana/doc/html/structboost_1_1hana_1_1type.html#a2d2e7e08e284f7e0bd1bd9c3ad0e0a2b
#include <boost/hana.hpp>
#include <vector>
#include <string>
namespace hana = boost::hana;
int main() {
// 检查成员
struct Person {
std::string name;
};
auto has_name = hana::is_valid([](auto &&p) -> decltype(p.name) {});
Person joe{"Joe"};
static_assert(has_name(joe), "");
static_assert(!has_name(1), "");
// 检查嵌套类型
auto has_value_type = hana::is_valid([](auto t) -> hana::type<typename decltype(t)::type::value_type> {});
static_assert(has_value_type(hana::type_c<std::vector<int>>), "");
static_assert(!has_value_type(hana::type_c<Person>), "");
return 0;
}

View File

@@ -0,0 +1,78 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "structData.h"
template<typename UnnamedType>
struct container {
// Let's put the test in private.
private:
// We use std::declval to 'recreate' an object of 'UnnamedType'.
// We use std::declval to also 'recreate' an object of type 'Param'.
// We can use both of these recreated objects to test the validity!
template<typename Param>
constexpr auto testValidity(int /* unused */)
-> decltype(std::declval<UnnamedType>()(std::declval<Param>()), std::true_type()) {
// If substitution didn't fail, we can return a true_type.
return std::true_type();
}
template<typename Param>
constexpr std::false_type testValidity(...) {
// Our sink-hole returns a false_type.
return std::false_type();
}
public:
// A public operator() that accept the argument we wish to test onto the UnnamedType.
// Notice that the return type is automatic!
template<typename Param>
constexpr auto operator()(const Param &p) {
// The argument is forwarded to one of the two overloads.
// The SFINAE on the 'true_type' will come into play to dispatch.
// Once again, we use the int for the precedence.
return testValidity<Param>(int());
}
};
template<typename UnnamedType>
constexpr auto is_valid(const UnnamedType &t) {
// We used auto for the return type: it will be deduced here.
return container<UnnamedType>();
}
// Check if a type has a serialize method.
auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {});
// Notice how I simply swapped the return type on the right?
template<class T>
auto serialize(T &obj)
-> typename std::enable_if<decltype(hasSerialize(obj))::value, std::string>::type {
return obj.serialize();
}
template<class T>
auto serialize(T &obj)
-> typename std::enable_if<!decltype(hasSerialize(obj))::value, std::string>::type {
return to_string(obj);
}
int main() {
A a;
B b;
C c;
auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {});
std::cout << test(a) << std::endl;
std::cout << test(b) << std::endl;
std::cout << test(c) << std::endl;
// The following lines work like a charm!
std::cout << serialize(a) << std::endl;
std::cout << serialize(b) << std::endl;
std::cout << serialize(c) << std::endl;
}

View File

@@ -0,0 +1,62 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "structData.h"
// Equivalent to:
struct l5UnamedType {
template<typename T>
auto operator()(T &t) const -> decltype(t.serialize()) // /!\ This signature is nice for a SFINAE!
{
return t.serialize();
}
};
void fun(A a,B b,C c) {
// ***** Simple lambda unamed type *****
auto l4 = [](int a, int b) { return a + b; };
std::cout << l4(4, 5) << std::endl; // Output 9.
// Equivalent to:
struct l4UnamedType {
int operator()(int a, int b) const {
return a + b;
}
};
l4UnamedType l4Equivalent = l4UnamedType();
std::cout << l4Equivalent(4, 5) << std::endl; // Output 9 too.
// ***** auto parameters lambda unnamed type *****
// b's type is automagically deduced!
auto l5 = [](auto &t) -> decltype(t.serialize()) { return t.serialize(); };
std::cout << l5(b) << std::endl; // Output: I am a B!
// std::cout << l5(a) << std::endl; // Error: no member named 'serialize' in 'A'.
l5UnamedType l5Equivalent = l5UnamedType();
std::cout << l5Equivalent(b) << std::endl; // Output: I am a B!
// std::cout << l5Equivalent(a) << std::endl; // Error: no member named 'serialize' in 'A'.
}
int main() {
A a;
B b;
C c;
auto l1 = [](B &b) { return b.serialize(); }; // Return type figured-out by the return statement.
auto l3 = [](B &b) -> std::string { return b.serialize(); }; // Fixed return type.
auto l2 = [](B &b) -> decltype(b.serialize()) { return b.serialize(); }; // Return type dependant to the B type.
std::cout << l1(b) << std::endl; // Output: I am a B!
std::cout << l2(b) << std::endl; // Output: I am a B!
std::cout << l3(b) << std::endl; // Output: I am a B!
fun(a,b,c);
}

View File

@@ -0,0 +1,14 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
void f(std::string s); // int can't be convert into a string.
void f(double d); // int can be implicitly convert into a double, so this version could be selected, but...
void f(int i); // ... this version using the type int directly is even more close!
int main() {
f(1); // Call f(int i);
}

View File

@@ -0,0 +1,20 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
std::string f(...) // Variadic functions are so "untyped" that...
{
return "...";
}
template<typename T>
std::string f(const T &t)// ...this templated function got the precedence!
{
return "T";
}
int main() {
std::cout << f<int>(1) << std::endl;
}

View File

@@ -0,0 +1,23 @@
//
// Created by light on 20-1-6.
//
/*
The compiler will try this overload since it's less generic than the variadic.
T will be replace by int which gives us void f(const int& t, int::iterator* b = nullptr);
int doesn't have an iterator sub-type, but the compiler doesn't throw a bunch of errors.
It simply tries the next overload.
编译器尝试此重载,因为模板化函数比可变参数函数更精确(通用)。T将被int取代这将使我们得到
void f(const int& t, int::iterator* b = nullptr); int 没有迭代器子类型,但是编译器不会抛出一堆错误。
它只是尝试下一个重载。
*/
template<typename T>
void f(const T &t, typename T::iterator *it = nullptr) {}
// The sink-hole.
void f(...) {}
int main() {
f(1); // Calls void f(...) { }
}

View File

@@ -0,0 +1,13 @@
//
// Created by light on 20-1-6.
//
// The compiler will be really unhappy when it will later discover the call to hahahaICrash.
// 当以后发现对hahahaICrash的调用时编译器将非常不满意。
template <typename T> void f(T t) { t.hahahaICrash(); }
void f(...) { } // The sink-hole wasn't even considered.
int main() {
f(1);
}

View File

@@ -0,0 +1,77 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "structData.h"
template <typename UnnamedType> struct container
{
// Let's put the test in private.
private:
// We use std::declval to 'recreate' an object of 'UnnamedType'.
// We use std::declval to also 'recreate' an object of type 'Param'.
// We can use both of these recreated objects to test the validity!
template <typename... Params> constexpr auto test_validity(int /* unused */)
-> decltype(std::declval<UnnamedType>()(std::declval<Params>()...), std::true_type())
{
// If substitution didn't fail, we can return a true_type.
return std::true_type();
}
template <typename... Params> constexpr std::false_type test_validity(...)
{
// Our sink-hole returns a false_type.
return std::false_type();
}
public:
// A public operator() that accept the argument we wish to test onto the UnnamedType.
// Notice that the return type is automatic!
template <typename... Params> constexpr auto operator()(Params&& ...)
{
// The argument is forwarded to one of the two overloads.
// The SFINAE on the 'true_type' will come into play to dispatch.
return test_validity<Params...>(int());
}
};
template <typename UnnamedType> constexpr auto is_valid(UnnamedType&& t)
{
// We used auto for the return type: it will be deduced here.
return container<UnnamedType>();
}
// Check if a type has a serialize method.
auto hasSerialize = is_valid([](auto &&x) -> decltype(x.serialize()) {});
// Notice how I simply swapped the return type on the right?
template<class T>
auto serialize(T &obj)
-> typename std::enable_if<decltype(hasSerialize(obj))::value, std::string>::type {
return obj.serialize();
}
template<class T>
auto serialize(T &obj)
-> typename std::enable_if<!decltype(hasSerialize(obj))::value, std::string>::type {
return to_string(obj);
}
int main() {
A a;
B b;
C c;
auto test = is_valid([](const auto &t) -> decltype(t.serialize()) {});
std::cout << test(a,b) << std::endl;
std::cout << test(b) << std::endl;
std::cout << test(c) << std::endl;
// The following lines work like a charm!
std::cout << serialize(a) << std::endl;
std::cout << serialize(b) << std::endl;
std::cout << serialize(c) << std::endl;
}

View File

@@ -0,0 +1,35 @@
//
// Created by light on 20-1-6.
//
#include <boost/hana.hpp>
#include <iostream>
#include <string>
#include "structData.h"
using namespace std;
namespace hana = boost::hana;
// 检查类型是否有一个serialize方法
auto hasSerialize = hana::is_valid([](auto &&x) -> decltype(x.serialize()) {});
// 序列化任意对象
template<typename T>
std::string serialize(T const &obj) {
return hana::if_(hasSerialize(obj),
[](auto &x) { return x.serialize(); },
[](auto &x) { return to_string(x); }
)(obj);
}
int main() {
A a;
B b;
C c;
std::cout << serialize(a) << std::endl;
std::cout << serialize(b) << std::endl;
std::cout << serialize(c) << std::endl;
}

View File

@@ -0,0 +1,17 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
typedef char type_test[42];
type_test &f() {}
int main() {
// In the following lines f won't even be truly called but we can still access to the size of its return type.
// Thanks to the "fake evaluation" of the sizeof operator.
char arrayTest[sizeof(f())];
std::cout << sizeof(f()) << std::endl; // Output 42.
}

View File

@@ -0,0 +1,18 @@
#include <iostream>
//
// Created by light on 20-1-6.
//
typedef char yes; // Size: 1 byte.
typedef yes no[2]; // Size: 2 bytes.
// Two functions using our type with different size.
yes &f1() {}
no &f2() {}
int main() {
std::cout << sizeof(f1()) << std::endl;
std::cout << sizeof(f2()) << std::endl;
std::cout << (sizeof(f1()) == sizeof(f2())) << std::endl; // Output 0.
}

View File

@@ -0,0 +1,30 @@
//
// Created by light on 20-1-6.
//
#ifndef MORDEN_C_STRUCTDATA_H
#define MORDEN_C_STRUCTDATA_H
// 类型A只有to_string 方法
struct A {
};
std::string to_string(const A &) {
return "I am A";
}
// 类型B有serialize方法
struct B {
std::string serialize() const {
return "I am B";
}
};
// 类型C有个serialize数据成员与to_string方法
struct C {
std::string serialize;
};
std::string to_string(const C &) {
return "I am C";
}
#endif //MORDEN_C_STRUCTDATA_H

View File

@@ -0,0 +1,81 @@
//
// Created by light on 20-1-6.
//
#include <iostream>
#include "structData.h"
template<class T>
struct hasSerialize {
// 编译时比较
typedef char yes[1];
typedef char no[2];
// 允许我们检查序列化确实是一种方法
// 第二个参数必须是第一个参数的类型
// 例如: reallyHas<int,10> 替换为 reallyHas<int,int 10> 并起作用
// 注意:它仅适用于整数常量和指针(因此函数指针可以使用)
// 例如reallyHas<std::string (C::*)(), &C::serialize> 替换为
// reallyHas<std::string (C::*)(), std::string (C::*)() &C::serialize> 并起作用
template<typename U, U u>
struct reallyHas;
// std::string (C::*)() 是函数指针声明
template<typename C>
static yes &test(reallyHas<std::string (C::*)(), &C::serialize> * /*unused*/) {}
// ()()const 函数指针 -> std::string serialize() const
template<typename C>
static yes &test(reallyHas<std::string (C::*)() const, &C::serialize> * /*unused*/) {}
// The famous C++ sink-hole.
// Note that sink-hole must be templated too as we are testing test<T>(0).
// If the method serialize isn't available, we will end up in this method.
template<typename>
static no &test(...) { /* dark matter */ }
//用作测试的返回值的常数。
//由于编译时评估的大小,因此实际上在这里完成了测试。
static const bool value = sizeof(test<T>(0)) == sizeof(yes);
};
template<bool B, class T = void> // Default template version.
struct enable_if {
}; // This struct doesn't define "type" and the substitution will fail if you try to access it.
template<class T> // A specialisation used if the expression is true.
struct enable_if<true, T> {
typedef T type;
}; // This struct do have a "type" and won't fail on access.
template<class T>
typename enable_if<hasSerialize<T>::value, std::string>::type serialize(const T &obj) {
return obj.serialize();
}
template<class T>
typename enable_if<!hasSerialize<T>::value, std::string>::type serialize(const T &obj) {
return to_string(obj);
}
int main() {
// Usage:
// enable_if<true, int>::type t1; // Compiler happy. t's type is int.
// enable_if<hasSerialize<B>::value, int>::type t2; // Compiler happy. t's type is int.
//
// enable_if<false, int>::type t3; // Compiler unhappy. no type named 'type' in 'enable_if<false, int>';
// enable_if<hasSerialize<A>::value, int>::type t4; // no type named 'type' in 'enable_if<false, int>';
A a;
B b;
C c;
// The following lines work like a charm!
std::cout << serialize(a) << std::endl;
std::cout << serialize(b) << std::endl;
std::cout << serialize(c) << std::endl;
}

View File

@@ -0,0 +1,54 @@
//
// Created by light on 19-12-28.
//
#include <iostream>
using namespace std;
// 使用template实现IF条件判断
template<bool cond,
typename Then,
typename Else>
struct IF;
template<typename Then,
typename Else>
struct IF<true, Then, Else> {
typedef Then result;
};
template<typename Then,
typename Else>
struct IF<false, Then, Else> {
typedef Else result;
};
// 判断奇数与偶数
template<int N>
struct isEven {
static const auto RES = IF<N & 1 == 0, true_type, false_type>::result::value;
};
template<int nums1, int nums2>
struct Add_ {
static const int value = nums1 + nums2;
};
template<int nums1, int nums2>
struct Sub_ {
static const int value = nums1 - nums2;
};
// 加减
template<bool cond, int nums1, int nums2>
struct addSub {
static const auto RES = IF<cond, Add_<nums1, nums2>, Sub_<nums1, nums2>>::result::value;
};
int main() {
cout << isEven<10>::RES << endl;
cout << addSub<true, 10, 2>::RES << endl;
}

View File

@@ -0,0 +1,77 @@
//
// Created by light on 20-1-5.
//
#include <iostream>
using namespace std;
// 使用template实现while循环
template<bool condition,
typename Body>
struct WhileLoop;
template<typename Body>
struct WhileLoop<true, Body> {
typedef typename WhileLoop<
Body::cond_value,
typename Body::next_type>::type
type;
};
template<typename Body>
struct WhileLoop<false, Body> {
typedef
typename Body::res_type type;
};
template<typename Body>
struct While {
typedef typename WhileLoop<Body::cond_value, Body>::type type;
};
template<typename Body>
using While_t = WhileLoop<Body::cond_value, Body>;
namespace my {
template<class T, T v>
struct integral_constant {
static const T value = v;
typedef T value_type;
typedef integral_constant<T, v> type;
};
}
template<int result, int n>
struct SumLoop {
// 循环的条件
static const bool cond_value =
n != 0;
// 循环后的结果
static const int res_value =
result;
// 循环时的状态
typedef my::integral_constant<
int, res_value>
res_type;
// 循环执行一次时的状态
typedef SumLoop<result + n, n - 1>
next_type;
};
template<int n>
struct Sum {
typedef SumLoop<0, n> type;
};
template<int n>
using Sum_t = SumLoop<0, n>;
int main() {
cout << While<Sum<6>::type>::type::value << endl;
cout << While_t<Sum_t<6>>::type::value << endl;
return 0;
}

Binary file not shown.

View File

@@ -0,0 +1,24 @@
//
// Created by light on 19-12-28.
//
#include <iostream>
using namespace std;
template<int n>
struct factorial {
static_assert(n >= 0, "Arg must be non-negative");
static const int value = n * factorial<n - 1>::value;
};
template<>
struct factorial<0> {
static const int value = 1;
};
int main() {
printf("%d\n", factorial<-1>::value);
return 0;
}

View File

@@ -0,0 +1,39 @@
//
// Created by light on 20-1-5.
//
#include <iostream>
#include <vector>
using namespace std;
template<
template<typename, typename>
class OutContainer = vector,
typename F, class R>
auto fmap(F &&f, R &&inputs) {
typedef decay_t<decltype(f(*inputs.begin()))> result_type;
OutContainer<result_type, allocator<result_type >> result;
for (auto &&item:inputs) {
result.push_back(f(item));
}
return result;
}
// 对每一个数进行加1操作
int add_1(int x) {
return x + 1;
}
int main() {
vector<int> v{1, 2, 3, 4, 5};
auto result = fmap(add_1, v);
for (auto item:result)
cout << item << " ";
cout<<endl;
return 0;
}

View File

@@ -0,0 +1,13 @@
# 编译期多态:泛型编程和模板入门
运行时多态通过利用接口或者虚函数实现,本节则主要阐述编译时多态。
>如果一只鸟走起来像鸭子、游起泳来像鸭子、叫起来也像鸭子,那么这只鸟就可以被当作鸭子。
鸭子类型使得开发者可以不使用继承体系来灵活地实现一些“约定”,尤其是使得混合不同来源、使用不同对象继承体系的代码成为可能。唯一的要求只是,这些不同的对象有“共通”的成员函数。这些成员函数应当有相同的名字和相同结构的参数(并不要求参数类型相同)。
在C++中实现鸭子类型可以通过模板或者说泛型编程。不管是类模板还是函数模板编译器在看到其定义时只能做最基本的语法检查真正的类型检查要在实例化instantiation的时候才能做。一般而言这也是编译器会报错的时候。
> “动态”多态和“静态”多态的对比
“动态”多态解决的是运行时的行为变化—这个是无法在编译时确定的。“静态”多态或者“泛型”—解决的是很不同的问题,让适用于不同类型的“同构”算法可以用同一套代码来实现,实际上强调的是对代码的复用。

View File

@@ -0,0 +1,82 @@
//
// Created by light on 19-12-27.
//
#include<iostream>
#include <vector>
#include <cstring>
using namespace std;
// 函数模板
template<typename T>
bool isEqual(T t1, T t2) {
cout << "函数模板" << endl;
return t1 == t2;
}
// 函数模板全特化
template<>
bool isEqual(const char *t1, const char *t2) {
cout << "函数模板全特化" << endl;
return strcmp(t1, t2);
}
// 函数模板偏特化 C++不支持函数模板偏特化 可以重载解决
//template<typename T>
//bool isEqual<T, int>(T a, double b) {
// cout << "函数模板偏特化" << endl;
// return a == b;
//}
// 改为重载 下面是重载!!! 而不是偏特化!
template<typename T>
bool isEqual(T a, double b) {
cout << "函数重载" << endl;
return a == b;
}
/// =============================================================
// 类模板
template<class T>
class comp {
public:
bool isEqual(T t1, T t2) {
cout << "类模板" << endl;
return t1 == t2;
}
};
// 类模板全特化
template<>
class comp<const char *> {
public:
bool isEqual(const char *t1, const char *t2) {
cout << "类模板全特化" << endl;
return strcmp(t1, t2);
}
};
// 类模板偏特化
template<typename T>
class comp<T *> {
public:
bool isEqual(T *t1, T *t2) {
cout << "类模板偏特化" << endl;
return strcmp(t1, t2);
}
};
int main() {
isEqual(1, 2); // 实例化的时候做类型检查
isEqual(1, 2.1);
// isEqual(1.1, 2.1); // 模糊的!
isEqual("qwqw", "asd");
comp<int> c;
c.isEqual(10, 11);
comp<const char *> cc;
cc.isEqual("he", "lo");
comp<char *> p;
p.isEqual("he", "ll");
}

Binary file not shown.

View File

@@ -0,0 +1,43 @@
//
// Created by light on 19-12-16.
//
#include <iostream>
#include <map>
#include <set>
#include <vector>
#include "output_container.h"
using namespace std;
int main() {
map<int, int> mp{
{1, 1},
{2, 4},
{3, 9}};
cout << mp << endl;
vector<vector<int>> vv{
{1, 1},
{2, 4},
{3, 9}};
cout << vv << endl;
pair<int, int> p{1, 2};
cout << p << endl;
set<int> s{1, 2, 3};
cout << s << endl;
vector<char> v{'a', 'b'};
cout << v << endl;
set<char *> vs{"a", "b"};
cout << vs << endl;
map<int, char *> mm{
{1, "23"},
{2, "234hi"}
};
cout << mm << endl;
}

View File

@@ -0,0 +1,78 @@
//
// Created by light on 20-1-7.
//
#include <iostream>
#include <vector>
#include <array>
using namespace std;
// C++17 内联变量
struct magic {
// static const int number = 42; // error
static constexpr int number = 42; // ok
// 类的静态constexpr成员变量默认就是内联的。
// static inline const int number = 42; // ok
// const常量和类外面的constexpr变量是不默认内联需要手工加inline关键字才会变成内联
};
// 内联变量之前用这个方法,称为one definition rule有了内联变量及模板则不受这条规则限制!
// const int magic::number = 42; // ok
// C++14 变量模板
// 之前我们需要用类静态数据成员来表达的东西,使用变量模板可以更简洁地表达。
namespace my {
template<class T>
inline constexpr bool
is_trivially_destructible_v =
std::is_trivially_destructible<
T>::value;
}
// 不重要析构
class A {
};
// 重要析构
class B {
~B() {}
};
int main() {
std::cout << magic::number << std::endl;
std::vector<int> v;
v.push_back(magic::number); // undefined reference to `magic::number'
std::cout << v[0] << std::endl;
// 不重要的析构
std::cout << is_trivially_destructible_v<A> << std::endl;
std::cout << is_trivially_destructible_v<B> << std::endl;
// constexpr变量仍是const
constexpr int a = 42;
const int &b = a;
// 上述const不能去掉,否则报如下错误:
// binding reference of type int& to const int discards qualifiers
// constexpr 构造函数和字面类型
// C++14 放开了 constexpr函数里的循环
// C++20 放开了 try...catch语句 和 asm声明
// constexpr函数里不能使用goto语句
// 一个有意思的情况是一个类的构造函数。如果一个类的构造函数里面只包含常量表达式、
// 满足对 constexpr 函数的限制的话(这也意味着,里面不可以有任何动态内存分配),
// 并且类的析构函数是平凡的,那这个类就可以被称为是一个字面类型。
std::array<int, 5> ay;
constexpr string_view sv{"hi"};
constexpr pair pr{sv[0], sv[1]};
constexpr array aa{pr.first, pr.second};
constexpr int n1 = aa[0];
constexpr int n2 = aa[1];
cout << n1 << ' ' << n2 << '\n';
}

View File

@@ -0,0 +1,150 @@
//
// Created by light on 20-1-8.
//
#ifndef MORDEN_C_OUTPUT_CONTAINER_H
#define MORDEN_C_OUTPUT_CONTAINER_H
#include <ostream> // std::ostream
#include <type_traits> // std::false_type/true_type/decay_t/is_same_v
#include <utility> // std::declval/pair
// 检测是否是pair
template<typename T>
struct is_pair : std::false_type {
};
template<typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type {
};
template<typename T>
inline constexpr bool is_pair_v = is_pair<T>::value;
// 检测是否可以直接输出
template<typename T>
struct has_output_function {
template<class U>
static auto output(U *ptr)
-> decltype(std::declval<std::ostream &>() << *ptr,
std::true_type());
template<class U>
static std::false_type output(...);
static constexpr bool value =
decltype(output<T>(nullptr))::value;
};
template<typename T>
inline constexpr bool has_output_function_v =
has_output_function<T>::value;
enum CHARS {
ORD, // 其他类型
CHAR, // char 类型
STRING // string 类型
};
template<typename T>
int ischarOrString(T &elem) {
using std::decay_t;
using std::is_same_v;
using element_type = decay_t<decltype(elem)>;
constexpr bool is_char_v = is_same_v<element_type, char>;
constexpr bool is_string_v = is_same_v<element_type, char *> ||
is_same_v<element_type, const char *> ||
is_same_v<element_type, std::string>;
if (is_char_v)
return CHAR;
else if (is_string_v)
return STRING;
else
return ORD;
}
template<typename T>
void output(T &elem, int type, std::ostream &os) {
switch (type) {
case CHAR:
os << '\'' << elem << '\'';
break;
case STRING:
os << '\"' << elem << '\"';
break;
case ORD:
os << elem;
break;
}
}
template<typename T, typename Cont>
auto output_element(std::ostream &os, const T &element,
const Cont &)
-> typename std::enable_if<is_pair<typename Cont::value_type>::value, bool>::type {
int ftype = ischarOrString(element.first);
int stype = ischarOrString(element.second);
output(element.first, ftype, os);
os << " => ";
output(element.second, stype, os);
return true;
}
template<typename T, typename Cont>
auto output_element(std::ostream &os, const T &element,
const Cont &)
-> typename std::enable_if<!is_pair<typename Cont::value_type>::value, bool>::type {
int etype = ischarOrString(element);
output(element, etype, os);
return false;
}
template<typename T, typename U>
std::ostream &operator<<(std::ostream &os, const std::pair<T, U> &pr) {
os << '(' << pr.first << ", " << pr.second << ')';
return os;
}
//template<typename T, typename Cont>
//auto output_element(std::ostream& os, const T& element,
// const Cont&, const std::true_type)
//-> decltype(std::declval<typename Cont::key_type>(), os)
//{
// os << element.first << " => " << element.second;
// return os;
//}
//
//template <typename T, typename Cont>
//auto output_element(std::ostream& os, const T& element,
// const Cont&, ...)
//-> decltype(os)
//{
// os << element;
// return os;
//}
// 针对没有输出函数的容器处理
template<typename T,
typename = std::enable_if_t<!has_output_function_v<T>>>
auto operator<<(std::ostream &os, const T &container)
-> decltype(container.begin(), container.end(), os) {
os << "{ ";
if (!container.empty()) {
bool on_first_element = true;
for (auto elem:container) {
if (!on_first_element) {
os << ", ";
} else {
on_first_element = false;
}
output_element(os, elem, container);
}
}
os << " }";
return os;
}
#endif //MORDEN_C_OUTPUT_CONTAINER_H

View File

@@ -0,0 +1,47 @@
//
// Created by light on 20-1-7.
//
#include <iostream>
#include <array>
int sqr(int n) {
return n * n;
}
int rand() {
return std::rand();
}
// constant expression
constexpr int sqr1(int n) {
return n * n;
}
constexpr int factorial(int n) {
// n是普通的int 不能使用static_assert
// static_assert(n>=0); //error
// 正确方式
if (n < 0) {
throw std::invalid_argument(
"Arg must be non-negative");
}
if (n == 0) {
return 1;
} else {
return n * factorial(n - 1);
}
}
int main() {
int a[sqr(3)]; // ok
const int n1 = sqr(3); // ok
constexpr int n = sqr1(3); // constexpr=constant expression 常量表达式
std::array<int, n> aa; // ok
std::array<int, factorial(10)> b; // ok
int cc[rand()];
return 0;
}

View File

@@ -0,0 +1,31 @@
//
// Created by light on 20-1-10.
//
#include <iostream>
class test3 {
public:
int value;
// constexpr const method - can't chanage the values of object fields and can be evaluated at compile time.
constexpr int getvalue() const {
return (value);
}
constexpr test3(int Value)
: value(Value) {
}
};
int main() {
// 加不加都行
constexpr test3 x(100); // OK. Constructor is constexpr.
int array[x.getvalue()]; // OK. x.getvalue() is constexpr and can be evaluated at compile time.
}

View File

@@ -0,0 +1,21 @@
//
// Created by light on 19-12-16.
//
#include <iostream>
#include <map>
#include <vector>
#include "output_container.h"
using namespace std;
int main()
{
map<int, int> mp{
{1, 1}, {2, 4}, {3, 9}};
cout << mp << endl;
vector<vector<int>> vv{
{1, 1}, {2, 4}, {3, 9}};
cout << vv << endl;
}

View File

@@ -0,0 +1,148 @@
/*
* Written by Wu Yongwei <wuyongwei AT gmail DOT com>.
*
* Using this file requires a C++17-compliant compiler.
*
* This is free and unencumbered software released into the public domain.
*
* Anyone is free to copy, modify, publish, use, compile, sell, or
* distribute this software, either in source code form or as a compiled
* binary, for any purpose, commercial or non-commercial, and by any
* means.
*
* In jurisdictions that recognize copyright laws, the author or authors
* of this software dedicate any and all copyright interest in the
* software to the public domain. We make this dedication for the benefit
* of the public at large and to the detriment of our heirs and
* successors. We intend this dedication to be an overt act of
* relinquishment in perpetuity of all present and future rights to this
* software under copyright law.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*
* For more information, please refer to <http://unlicense.org>
*
*/
#ifndef OUTPUT_CONTAINER_H
#define OUTPUT_CONTAINER_H
#include <ostream> // std::ostream
#include <type_traits> // std::false_type/true_type/decay_t/is_same_v
#include <utility> // std::declval/pair
// Type trait to detect std::pair
template <typename T>
struct is_pair : std::false_type {};
template <typename T, typename U>
struct is_pair<std::pair<T, U>> : std::true_type {};
template <typename T>
inline constexpr bool is_pair_v = is_pair<T>::value;
// Type trait to detect whether an output function already exists
template <typename T>
struct has_output_function {
template <class U>
static auto output(U* ptr)
-> decltype(std::declval<std::ostream&>() << *ptr,
std::true_type());
template <class U>
static std::false_type output(...);
static constexpr bool value =
decltype(output<T>(nullptr))::value;
};
template <typename T>
inline constexpr bool has_output_function_v =
has_output_function<T>::value;
/* NB: Visual Studio 2017 (or below) may have problems with
* has_output_function_v<T>: you should then use
* has_output_function<T>::value instead, or upgrade to
* Visual Studio 2019. */
// Output function for std::pair
template <typename T, typename U>
std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& pr);
// Element output function for containers that define a key_type and
// have its value type as std::pair
template <typename T, typename Cont>
auto output_element(std::ostream& os, const T& element,
const Cont&, const std::true_type)
-> decltype(std::declval<typename Cont::key_type>(), os);
// Element output function for other containers
template <typename T, typename Cont>
auto output_element(std::ostream& os, const T& element,
const Cont&, ...)
-> decltype(os);
// Main output function, enabled only if no output function already exists
template <typename T,
typename = std::enable_if_t<!has_output_function_v<T>>>
auto operator<<(std::ostream& os, const T& container)
-> decltype(container.begin(), container.end(), os)
{
using std::decay_t;
using std::is_same_v;
using element_type = decay_t<decltype(*container.begin())>;
constexpr bool is_char_v = is_same_v<element_type, char>;
if constexpr (!is_char_v) {
os << "{ ";
}
if (!container.empty()) {
auto end = container.end();
bool on_first_element = true;
for (auto it = container.begin(); it != end; ++it) {
if constexpr (is_char_v) {
if (*it == '\0') {
break;
}
}
if constexpr (!is_char_v) {
if (!on_first_element) {
os << ", ";
} else {
on_first_element = false;
}
}
output_element(os, *it, container, is_pair<element_type>());
}
}
if constexpr (!is_char_v) {
os << " }";
}
return os;
}
template <typename T, typename Cont>
auto output_element(std::ostream& os, const T& element,
const Cont&, const std::true_type)
-> decltype(std::declval<typename Cont::key_type>(), os)
{
os << element.first << " => " << element.second;
return os;
}
template <typename T, typename Cont>
auto output_element(std::ostream& os, const T& element,
const Cont&, ...)
-> decltype(os)
{
os << element;
return os;
}
template <typename T, typename U>
std::ostream& operator<<(std::ostream& os, const std::pair<T, U>& pr)
{
os << '(' << pr.first << ", " << pr.second << ')';
return os;
}
#endif // OUTPUT_CONTAINER_H

View File

@@ -0,0 +1,64 @@
//
// Created by light on 19-12-16.
//
#include <vector>
#include <iostream>
#include <queue>
using namespace std;
class Obj1 {
public:
Obj1()
{
cout << "Obj1()\n";
}
Obj1(const Obj1&)
{
cout << "Obj1(const Obj1&)\n";
}
Obj1(Obj1&&)
{
cout << "Obj1(Obj1&&)\n";
}
};
class Obj2 {
public:
Obj2()
{
cout << "Obj2()\n";
}
Obj2(const Obj2&)
{
cout << "Obj2(const Obj2&)\n";
}
Obj2(Obj2&&) noexcept
{
cout << "Obj2(Obj2&&)\n";
}
};
int main()
{
vector<int> v;
int nums = 20;
for (int i = 0; i < nums; ++i) {
v.push_back(i + 1);
cout << "v_size: " << v.size() << "\t v_capacity: " << v.capacity() << endl;
}
// 头两个在已有空间上成功构造。第三个时发现空间不足,系统会请求更大的空间,大小由实现决定(比如两倍)。
// 有了足够的空间后就会在新空间的第三个的位置构造第三个obj1成功之后再把头两个拷贝或移动过来。
vector<Obj1> v1;
// v1.reserve(2);
v1.emplace_back();
v1.emplace_back();
v1.emplace_back();
v1.emplace_back();
vector<Obj2> v2;
v2.reserve(2);
v2.emplace_back();
v2.emplace_back();
v2.emplace_back();
}

View File

@@ -0,0 +1,53 @@
//
// Created by light on 19-12-16.
//
#include <iostream>
#include <map> // std::map
#include "../container1/output_container.h"
using namespace std;
#define ARRAY_LEN(a) \
(sizeof(a) / sizeof((a)[0]))
void test(int a[8]) {
cout << ARRAY_LEN(a) << endl;
}
void test1(int arr[]) {
// 不能编译
// std::cout << std::size(arr)
// << std::endl;
}
typedef char mykey_t[8];
typedef std::array<char, 8> mykey_t1;
int main() {
int a[8];
test(a);
// C++17 直接提供了一个 size 方法,可以用于提供数组长度,
int arr[] = {1, 2, 3, 4, 5};
std::cout << "The array length is "
<< std::size(arr)
<< std::endl;
// 并且在数组退化成指针的情况下会直接失败
test1(arr);
std::map<mykey_t, int> mp;
mykey_t mykey{"hello"};
// mp[mykey] = 5;
// 轰,大段的编译错误
std::map<mykey_t1, int> mp1;
mykey_t1 mykey1{"hello"};
mp1[mykey1] = 5; // ok
cout << mp1 << endl;
}

View File

@@ -0,0 +1,46 @@
//
// Created by light on 19-12-16.
//
#include <algorithm> // std::sort
#include <functional> // std::less/greater/hash
#include <iostream> // std::cout/endl
#include <string> // std::string
#include <vector> // std::vector
#include "../container1/output_container.h"
using namespace std;
int main()
{
// 初始数组
vector<int> v{13, 6, 4, 11, 29};
cout << v << endl;
// 从小到大排序
sort(v.begin(), v.end());
cout << v << endl;
// 从大到小排序
sort(v.begin(), v.end(),
greater<int>());
cout << v << endl;
cout << hex;
auto hp = hash<int*>();
cout << "hash(nullptr) = "
<< hp(nullptr) << endl;
cout << "hash(v.data()) = "
<< hp(v.data()) << endl;
cout << "v.data() = "
<< static_cast<void*>(v.data())
<< endl;
auto hs = hash<string>();
cout << "hash(\"hello\") = "
<< hs(string("hello")) << endl;
cout << "hash(\"hellp\") = "
<< hs(string("hellp")) << endl;
}

View File

@@ -0,0 +1,30 @@
//
// Created by light on 19-12-16.
//
#include <functional> // std::greater
#include <iostream> // std::cout/endl
#include <memory> // std::pair
#include <queue> // std::priority_queue
#include <vector> // std::vector
#include "../container1/output_container.h"
using namespace std;
int main()
{
priority_queue<
pair<int, int>,
vector<pair<int, int>>,
greater<pair<int, int>>>
q;
q.push({1, 1});
q.push({2, 2});
q.push({0, 3});
q.push({9, 4});
while (!q.empty()) {
cout << q.top() << endl;
q.pop();
}
}

View File

@@ -0,0 +1,73 @@
//
// Created by light on 19-12-16.
//
#include <iostream>
#include <functional>
#include <map>
#include <set>
#include <string>
#include "../container1/output_container.h"
#include <tuple>
using namespace std;
int main() {
set<int> s{1, 1, 1, 2, 3, 4};
cout << s << endl;
multiset<int, greater<int>> ms{1, 1, 1, 2, 3, 4};
cout << ms << endl;
map<string, int> mp{
{"one", 1},
{"two", 2},
{"three", 3},
{"four", 4}
};
cout << mp << endl;
mp.insert({"four", 4});
cout << mp << endl;
cout << (mp.find("four") == mp.end()) << endl;
cout << (mp.find("five") == mp.end()) << endl;
mp["five"] = 5;
cout << mp << endl;
multimap<string, int> mmp{
{"one", 1},
{"two", 2},
{"three", 3},
{"four", 4}
};
cout << mmp << endl;
mmp.insert({"four", -4});
cout << mmp << endl;
cout << (mp.find("four")->second) << endl;
cout << (mp.lower_bound("four")->second) << endl;
cout << (mp.upper_bound("four")->second) << endl;
cout << ((--mp.upper_bound("four"))->second) << endl;
multimap<string, int>::iterator
lower, upper;
std::tie(lower, upper) =
mmp.equal_range("four");
cout << (lower != upper) << endl; // 检测区间非空
cout << lower->second << endl;
cout << (--upper)->second << endl;
}

View File

@@ -0,0 +1,41 @@
//
// Created by light on 19-12-16.
//
#include <complex> // std::complex
#include <iostream> // std::cout/endl
#include <unordered_map> // std::unordered_map
#include <unordered_set> // std::unordered_set
#include "../container1/output_container.h"
using namespace std;
namespace std {
template <typename T>
struct hash<complex<T>> {
size_t
operator()(const complex<T>& v) const
noexcept
{
hash<T> h;
return h(v.real()) + h(v.imag());
}
};
} // namespace std
int main()
{
unordered_set<int> s{
1, 1, 2, 3, 5, 8, 13, 21
};
cout << s << endl;
unordered_map<complex<double>,
double>
umc{{{1.0, 1.0}, 1.4142},
{{3.0, 4.0}, 5.0}};
cout << umc << endl;
}

View File

@@ -0,0 +1,21 @@
//
// Created by light on 19-12-17.
//
#include <vector>
#include <iostream>
// “首先是内存分配。如果 new 出错,按照 C++ 的规则,一般会得到异常 bad_alloc对象的构造也就失败了。
// 这种情况下,在 catch 捕捉到这个异常之前,所有的栈上对象会全部被析构,资源全部被自动清理。”
// 之所以是栈上对象会全被被析构原因是堆上的东西都是由栈上的变量所引用的,栈上对象析构的过程,
// 堆上相应的资源自然就被释放了。而且被释放的对象的范围还被栈帧限定了。
// -fexceptions缺省开启
// g++ test.cpp -std=c++17 -fno-exceptions
// 关闭异常,可看到可执行文件的大小的变化。
int main()
{
std::vector<int> v{1, 2, 3, 4, 5};
v.push_back(20);
}

View File

@@ -0,0 +1,34 @@
//
// Created by light on 20-1-11.
//
#include <iostream>
using namespace std;
struct adder {
adder(int n) : n_(n) {}
int operator()(int x) const {
return x + n_;
}
private:
int n_;
};
int main() {
auto add_2 = adder(2);
// x+2
cout << add_2(3) << endl;
auto t = bind1st(plus<int>(), 2);
cout << t(1) << endl;
// 上述的C++98
binder2nd<plus<int> > a2(plus<int>(), 2);
cout << a2(3) << endl;
cout << [](int x) { return x * x; }(3) << endl;
return 0;
// lambda表达式默认就是constexpr函数
}

View File

@@ -0,0 +1,64 @@
//
// Created by light on 20-1-11.
//
#include <chrono>
#include <iostream>
#include <sstream>
#include <thread>
using namespace std;
int get_count() {
static int count = 0;
return ++count;
}
class task {
public:
task(int data) : data_(data) {}
/**
* this 标明按引用捕获外围对象(针对 lambda 表达式定义出现在一个非静态类成员内的情况);
* 注意默认捕获符 = 和 & 号可以自动捕获 this并且在 C++20 之前,在 = 后写 this 会导致出错)
* 本例子两次都按照第二次输出(this_thread::sleep_for(100ms);
* this 标明按引用捕获外围对象
*
*
* *this 标明按值捕获外围对象(针对 lambda 表达式定义出现在一个非静态类成员内的情况C++17 新增语法)
* 本例子正常输出
*/
auto lazy_launch() {
return
[*this, count = get_count()]()
mutable {
ostringstream oss;
oss << "Done work " << data_
<< " (No. " << count
<< ") in thread "
<< this_thread::get_id()
<< '\n';
msg_ = oss.str();
calculate();
};
}
void calculate() {
this_thread::sleep_for(100ms);
cout << msg_;
}
private:
int data_;
string msg_;
};
int main() {
auto t = task{37};
thread t1{t.lazy_launch()};
thread t2{t.lazy_launch()};
t1.join();
t2.join();
}

View File

@@ -0,0 +1,32 @@
//
// Created by light on 20-1-11.
//
#include <map>
#include <iostream>
#include <functional>
using namespace std;
int main() {
map<string, function<int(int, int)>>
op_dict{
{"+",
[](int x, int y) {
return x + y;
}},
{"-",
[](int x, int y) {
return x - y;
}},
{"*",
[](int x, int y) {
return x * y;
}},
{"/",
[](int x, int y) {
return x / y;
}},
};
}

View File

@@ -0,0 +1,13 @@
//
// Created by light on 19-12-25.
//
#include <iostream>
#include <cassert>
int main() {
const int alignment=5;
assert((alignment & (alignment - 1)) == 0);
static_assert((alignment & (alignment - 1)) == 0, "Alignment must be power of two");
return 0;
}

View File

@@ -0,0 +1,18 @@
//
// Created by light on 19-12-25.
//
class myFun {
public:
myFun() = default;
myFun(const myFun &) = default;
myFun &operator=(const myFun &) = default;
myFun(myFun &&) = delete;
myFun &operator=(myFun &&) = delete;
~myFun() = default;
};

View File

@@ -0,0 +1,81 @@
//
// Created by light on 19-12-25.
//
#include <chrono>
#include <complex>
#include <iostream>
#include <string>
#include <thread>
#include <bitset>
using namespace std::literals::chrono_literals;
using namespace std::literals::string_literals;
using namespace std::literals::complex_literals;
struct length {
double value;
enum unit {
metre,
kilometre,
millimetre,
centimetre,
inch,
foot,
yard,
mile,
};
static constexpr double factors[] =
{1.0, 1000.0, 1e-3,
1e-2, 0.0254, 0.3048,
0.9144, 1609.344};
explicit length(double v,
unit u = metre) {
value = v * factors[u];
}
};
length operator+(length lhs,
length rhs) {
return length(lhs.value +
rhs.value);
}
length operator "" _m(long double v) { return length(v, length::metre); }
length operator "" _cm(long double v) { return length(v, length::centimetre); }
// 可能有其他运算符
int main() {
std::cout << "i * i = " << 1i * 1i << std::endl;
std::cout << "Waiting for 500ms" << std::endl;
std::this_thread::sleep_for(500ms);
std::cout << "Hello world"s.substr(0, 5) << std::endl;
length l1 = length(1.0, length::metre);
length l2 = length(1.0, length::centimetre);
std::cout << l2.value << std::endl;
std::cout << (l1 + l2).value << std::endl;
// 1.0_m + 10.0_cm
std::cout << (1.0_m + 1.0_cm).value << std::endl;
// 二进制字面量
unsigned mask = 0b1101;
// 以十进制打印
std::cout << mask << std::endl;
// 打印二进制字面量
std::cout << std::bitset<4>(mask) << std::endl;
// 数字分隔符
unsigned mk = 0b111'000'000;
double pi = 3.141'5926;
return 0;
}

View File

@@ -0,0 +1,30 @@
//
// Created by light on 19-12-25.
//
class A {
public:
virtual void foo();
virtual void bar();
void foobar();
};
class B : public A {
public:
void foo() override; // OK
void bar() override final; // OK
//void foobar() override;
// 非虚函数不能 override
};
class C final : public B {
public:
void foo() override; // OK
//void bar() override;
// final 函数不可 override
};
class D : public C {
// 错误final 类不可派生
};

View File

@@ -0,0 +1,46 @@
//
// Created by light on 20-2-7.
//
#include <iostream>
using namespace std;
#include <mutex>
#define barrier() __asm__ volatile ("lwsync")
// method 1 operator new + placement new
//singleton *instance() {
// if (p == nullptr) {
// lock_guard<mutex> guard(lock_);
// if (p == nullptr) {
// singleton *tmp = static_cast<singleton *>(operator new(sizeof(singleton)));
// new(p)singleton();
// p = tmp;
// }
// }
// return p;
//}
class singleton {
private:
singleton() {}
static singleton *p;
static mutex lock_;
public:
static singleton *instance();
};
singleton *singleton::p = nullptr;
singleton *singleton::instance() {
if (p == nullptr) {
lock_guard<mutex> guard(lock_);
barrier();
if (p == nullptr) {
p = new singleton();
}
}
return p;
}

View File

@@ -0,0 +1,46 @@
//
// Created by light on 20-2-7.
//
#include <iostream>
using namespace std;
#include <mutex>
#include <atomic>
//C++ 11版本之后的跨平台实现
class singleton {
private:
singleton() {}
static mutex lock_;
static atomic<singleton *> p;
public:
singleton *instance();
};
mutex singleton::lock_;
atomic<singleton *> singleton::p;
/*
* std::atomic_thread_fence(std::memory_order_acquire);
* std::atomic_thread_fence(std::memory_order_release);
* 这两句话可以保证他们之间的语句不会发生乱序执行。
*/
singleton *singleton::instance() {
singleton *tmp = p.load(memory_order_relaxed);
atomic_thread_fence(memory_order_acquire);
if (tmp == nullptr) {
lock_guard<mutex> guard(lock_);
tmp = p.load(memory_order_relaxed);
if (tmp == nullptr) {
tmp = new singleton();
atomic_thread_fence(memory_order_release);
p.store(tmp, memory_order_relaxed);
}
}
return p;
}

View File

@@ -0,0 +1,42 @@
//
// Created by light on 20-2-7.
//
#include <iostream>
using namespace std;
#include <mutex>
class singleton {
private:
singleton() {}
static singleton *p;
static mutex lock_;
public:
singleton *instance();
// 实现一个内嵌垃圾回收类
class CGarbo
{
public:
~CGarbo()
{
if(singleton::p)
delete singleton::p;
}
};
static CGarbo Garbo; // 定义一个静态成员变量,程序结束时,系统会自动调用它的析构函数从而释放单例对象
};
singleton *singleton::p = nullptr;
singleton::CGarbo Garbo;
singleton* singleton::instance() {
if (p == nullptr) {
lock_guard<mutex> guard(lock_);
if (p == nullptr)
p = new singleton();
}
return p;
}

View File

@@ -0,0 +1,17 @@
//
// Created by light on 20-2-6.
//
class singleton {
private:
singleton() {}
static singleton *p;
public:
static singleton *instance();
};
singleton *singleton::p = new singleton();
singleton* singleton::instance() {
return p;
}

View File

@@ -0,0 +1,19 @@
//
// Created by light on 20-2-6.
//
class singleton {
private:
singleton() {}
static singleton *p;
public:
static singleton *instance();
};
singleton *singleton::p = nullptr;
singleton* singleton::instance() {
if (p == nullptr)
p = new singleton();
return p;
}

View File

@@ -0,0 +1,25 @@
//
// Created by light on 20-2-7.
//
#include <iostream>
using namespace std;
#include <mutex>
class singleton {
private:
singleton() {}
static singleton *p;
static mutex lock_;
public:
static singleton *instance();
};
singleton *singleton::p = nullptr;
singleton* singleton::instance() {
lock_guard<mutex> guard(lock_);
if (p == nullptr)
p = new singleton();
return p;
}

View File

@@ -0,0 +1,28 @@
//
// Created by light on 20-2-6.
//
#include <sys/param.h>
#include <pthread.h>
class singleton {
private:
singleton(); //私有构造函数,不允许使用者自己生成对象
singleton(const singleton &other);
//要写成静态方法的原因类成员函数隐含传递this指针第一个参数
static void init() {
p = new singleton();
}
static pthread_once_t ponce_;
static singleton *p; //静态成员变量
public:
singleton *instance() {
// init函数只会执行一次
pthread_once(&ponce_, &singleton::init);
return p;
}
};

View File

@@ -0,0 +1,22 @@
//
// Created by light on 20-2-7.
//
#include <iostream>
using namespace std;
class singleton {
private:
static singleton *p;
singleton() {}
public:
singleton *instance();
};
singleton *singleton::instance() {
static singleton p;
return &p;
}

View File

@@ -0,0 +1,53 @@
//
// Created by light on 19-12-15.
//
#include <iostream>
using namespace std;
template<typename T>
void f(T &&param) {
static_assert(std::is_lvalue_reference<T>::value, "T& is lvalue reference");
cout << "T& is lvalue reference" << endl;
}
template<typename T>
class Widget {
typedef T& LvalueRefType;
typedef T&& RvalueRefType;
public:
void judge() {
static_assert(std::is_lvalue_reference<LvalueRefType>::value, "LvalueRefType & is lvalue reference");
static_assert(std::is_lvalue_reference<RvalueRefType>::value, "RvalueRefType & is lvalue reference");
cout << "LvalueRefType and RvalueRefType is lvalue reference" << endl;
}
void f(LvalueRefType&& param) {
}
};
int main() {
int x;
int &&r1 = 10;
int &r2 = x;
f(r1);
f(r2);
Widget<int &> w;
w.judge();
Widget<int> w1, w2;
auto&& v1 = w1; // v1 is an auto-based universal reference being
// initialized with an lvalue, so v1 becomes an
// lvalue reference referring to w1.
// 不能编译
// decltype(w1)&& v2 = w2; // v2 is a decltype-based universal reference, and
// decltype(w1) is Widget, so v2 becomes an rvalue reference.
// w2 is an lvalue, and its not legal to initialize an
// rvalue reference with an lvalue, so
// this code does not compile.
}

View File

@@ -0,0 +1,55 @@
//
// Created by light on 19-12-15.
//
#include <iostream> // std::cout/endl
#include <utility> // std::move
using namespace std;
class Obj {
public:
Obj()
{
cout << "Obj()" << endl;
}
Obj(const Obj&)
{
cout << "Obj(const Obj&)"
<< endl;
}
Obj(Obj&&)
{
cout << "Obj(Obj&&)" << endl;
}
};
Obj simple()
{
Obj obj;
// 简单返回对象;一般有 NRVO
return obj;
}
Obj simple_with_move()
{
Obj obj;
// move 会禁止 NRVO
return std::move(obj);
}
Obj complicated(int n)
{
Obj obj1;
Obj obj2;
// 有分支,一般无 NRVO
if (n % 2 == 0) {
return obj1;
} else {
return obj2;
}
}
int main()
{
cout << "*** 1 ***" << endl;
auto obj1 = simple();
cout << "*** 2 ***" << endl;
auto obj2 = simple_with_move();
cout << "*** 3 ***" << endl;
auto obj3 = complicated(42);
}

View File

@@ -0,0 +1,45 @@
//
// Created by light on 19-12-15.
//
#include <iostream>
#include "../RAII/shape.h"
void overloaded( int const &arg ) { std::cout << "by lvalue\n"; }
void overloaded( int && arg ) { std::cout << "by rvalue\n"; }
template< typename t >
/* "t &&" with "t" being template param is special, and adjusts "t" to be
(for example) "int &" or non-ref "int" so std::forward knows what to do. */
void forwarding( t && arg ) {
std::cout << "via std::forward: ";
overloaded( std::forward< t >( arg ) );
std::cout << "via std::move: ";
overloaded( std::move( arg ) ); // conceptually this would invalidate arg
std::cout << "by simple passing: ";
overloaded( arg );
}
void foo(const shape&)
{
puts("foo(const shape&)");
}
void foo(shape&&)
{
puts("foo(shape&&)");
}
template <typename T>
void bar(T&& s)
{
foo(std::forward<T>(s));
}
int main() {
std::cout << "initial caller passes rvalue:\n";
forwarding( 5 );
std::cout << "initial caller passes lvalue:\n";
int x = 5;
forwarding( x );
circle temp;
bar(temp);
bar(circle());
}

View File

@@ -0,0 +1,108 @@
//
// Created by light on 19-12-15.
//
#include <iostream>
using namespace std;
class shape {
public:
shape() { cout << "shape" << endl; }
~shape() {
cout << "~shape" << endl;
}
};
class circle : public shape {
public:
circle() { cout << "circle" << endl; }
~circle() {
cout << "~circle" << endl;
}
};
class triangle : public shape {
public:
triangle() { cout << "triangle" << endl; }
~triangle() {
cout << "~triangle" << endl;
}
};
class rectangle : public shape {
public:
rectangle() { cout << "rectangle" << endl; }
~rectangle() {
cout << "~rectangle" << endl;
}
};
class result {
public:
result() { puts("result()"); }
~result() { puts("~result()"); }
};
result process_shape(const shape &shape1, const shape &shape2) {
puts("process_shape()");
return result();
}
class Base {
public:
Base() {
cout << "Base()" << endl;
}
~Base() {
cout << "~Base()" << endl;
}
};
class Derived : public Base {
public:
Derived() {
cout << "Derived()" << endl;
}
~Derived() {
cout << "~Derived()" << endl;
}
};
string f() { return "abc"; }
void g() {
const string &s = f(); // still legal?
cout << s << endl;
}
Derived factory() {
return Derived();
}
int main() {
process_shape(circle(), triangle());
cout << endl;
// 临时对象延迟
// result &&r = process_shape(circle(), triangle());
// 临时对象延迟只对rvalue有用而对xvalue无用
// result &&r = std::move(process_shape(circle(), triangle()));
// const Base &b1 = factory();
Base *b1 = new Derived;
delete b1;
cout<<endl;
Derived d;
Base &b2 =d;
}

View File

@@ -0,0 +1,43 @@
//
// Created by light on 19-12-14.
//
#include <iostream>
#include <functional>
using namespace std;
// xvalue
int&& f(){
return 3;
}
struct As
{
int i;
};
As&& ff(){
return As();
}
int main() {
// lvalue
int x = 0;
cout << "(x).addr = " << &x << endl;
cout << "(x = 1).addr = " << &(x = 1) << endl;
cout << "(++x).addr = " << &++x << endl;
cout << "(cout << ' ').addr=" << &(cout << ' ') << endl;
cout << "(\"hello\").addr=" << &("hello") << endl;
// rvalue
cout<<true<<endl;
// xvalue
f(); // The expression f() belongs to the xvalue category, because f() return type is an rvalue reference to object type.
static_cast<int&&>(7); // The expression static_cast<int&&>(7) belongs to the xvalue category, because it is a cast to an rvalue reference to object type.
std::move(7); // std::move(7) is equivalent to static_cast<int&&>(7).
ff().i; // The expression f().i belongs to the xvalue category, because As::i is a non-static data member of non-reference type, and the subexpression f() belongs to the xvlaue category.
return 0;
}

View File

@@ -0,0 +1,97 @@
//
// Created by light on 19-12-23.
//
#include <iostream>
#include <vector>
#include <string>
struct Test {
Test()
{
//std::cout << "construct a Test object" << std::endl;
}
Test(const Test&)
{
std::cout << "copy construct a Test object" << std::endl;
}
Test& operator=(const Test &t)
{
std::cout << "copy assignment a Test object" << std::endl;
return *this;
}
Test(Test&& t)
{
std::cout << "move construct a Test object" << std::endl;
}
Test& operator=(Test &&t)
{
std::cout << "move assignment a Test object" << std::endl;
return *this;
}
~Test()
{
//std::cout << "destruct a Test object" << std::endl;
}
};
Test getTest()
{
// anonymous object
return Test();
}
Test getTestWithName()
{
// named return value
Test temp;
return temp;
}
/**
* 1.copy construct本身在RVO和NRVO两种情况下被优化了如果再加上move反而画蛇添足。
* 2.加入了move assignment后默认是调用move assignment而不是copy assignment可以将move assignment注释后测试。
* 3.对于RVO和NRVO来说construction的情况编译器优化得比较好了加入move语义主要是对于assignment有比较大影响
*/
int main()
{
std::cout << "==== common case ====" << std::endl;
Test o1;
std::cout << "---- Test copy construct: " << std::endl;
Test o2(o1); // two ways for copy construct
Test o3 = o1;
std::cout << "---- Test move construct: " << std::endl;
Test o4(std::move(o3));
std::cout << "---- Test assignment: " << std::endl;
o2 = o1;
std::cout << "---- Test move assignment: " << std::endl;
Test o5; o5 = std::move(o4);
std::cout << "\n==== test for rvo ===" << std::endl;
std::cout << "---- Test rvo for copy construct: " << std::endl;
Test obj11(getTest());
Test obj1 = getTest();
std::cout << "---- Test rvo for move construct: " << std::endl;
Test obj12(std::move(getTest()));
std::cout << "---- Test rvo for assignment: " << std::endl;
Test obj2; obj2 = getTest();
std::cout << "---- Test rvo move assignment: " << std::endl;
Test obj5; obj5 = std::move(getTest());
std::cout << "\n==== test for nrvo ===" << std::endl;
std::cout << "---- Test nrvo for copy construct: " << std::endl;
Test obj33(getTestWithName());
Test obj3 = getTestWithName();
std::cout << "---- Test nrvo for move construct: " << std::endl;
Test obj34(std::move(getTestWithName()));
std::cout << "---- Test nrvo for assignment: " << std::endl;
Test obj4; obj4 = getTestWithName();
std::cout << "---- Test nrvo move assignment: " << std::endl;
Test obj6; obj6 = std::move(getTestWithName());
return 0;
}

View File

@@ -0,0 +1,49 @@
//
// Created by light on 19-12-22.
//
// rvo example
#include <iostream>
using namespace std;
// Can copy and move
class A {
public:
A() { cout << "Create A\n"; }
~A() { cout << "Destroy A\n"; }
A(const A &) { cout << "Copy A\n"; }
A(A &&) { cout << "Move A\n"; }
A& operator=(const A&a)
{
std::cout << "copy assignment" << std::endl;
return *this;
}
A& operator=(A &&a) {
cout << "move assignment\n";
return *this;
}
};
// 编译器可以优化 返回值移动出去
A getA_unnamed() {
return A();
}
int main() {
// cout<<"构造"<<endl;
// auto a = getA_unnamed();
cout<<"赋值"<<endl;
A aa;
aa=getA_unnamed();
// aa=std::move(getA_unnamed());
}

View File

@@ -0,0 +1,48 @@
//
// Created by light on 19-12-22.
//
// nrvo example
#include <iostream>
using namespace std;
// Can copy and move
class A {
public:
A() { cout << "Create A\n"; }
~A() { cout << "Destroy A\n"; }
A(const A &) { cout << "Copy A\n"; }
A(A &&) { cout << "Move A\n"; }
A& operator=(const A&a)
{
std::cout << "copy assignment" << std::endl;
return *this;
}
A& operator=(A &&a) {
cout << "move assignment\n";
return *this;
}
};
// 编译器可以优化 返回值移动出去
A getA_named() {
A a;
return a;
}
int main() {
// cout<<"拷贝"<<endl;
// auto a = getA_named();
cout<<"赋值"<<endl;
A aa;
aa=getA_named();
}

View File

@@ -0,0 +1,51 @@
//
// Created by light on 19-12-22.
//
#include <iostream>
using namespace std;
// Can copy and move
class A {
public:
A() { cout << "Create A\n"; }
~A() { cout << "Destroy A\n"; }
A(const A &) { cout << "Copy A\n"; }
A(A &&) { cout << "Move A\n"; }
A& operator=(const A&a)
{
std::cout << "copy assignment" << std::endl;
return *this;
}
A& operator=(A &&a) {
cout << "move assignment\n";
return *this;
}
};
// 编译器无法优化
A getA_duang() {
A a1;
A a2;
if (rand() > 42) {
return a1;
}
else {
return a2;
}
}
int main() {
// cout<<"拷贝"<<endl;
// auto a = getA_duang();
cout<<"赋值"<<endl;
A aa;
aa=getA_duang();
}

View File

@@ -0,0 +1,35 @@
//
// Created by light on 19-12-22.
//
#include <iostream>
using namespace std;
// Can copy and move
class A {
public:
A() { cout << "Create A\n"; }
~A() { cout << "Destroy A\n"; }
A(const A &) { cout << "Copy A\n"; }
A(A&&)= delete;
};
A getA_duang() {
A a1;
A a2;
if (rand() > 42) {
return a1;
}
else {
return a2;
}
}
int main() {
auto a = getA_duang();
}

View File

@@ -0,0 +1,31 @@
//
// Created by light on 19-12-22.
//
#include <iostream>
using namespace std;
// Can copy and move
class A {
public:
A() { cout << "Create A\n"; }
~A() { cout << "Destroy A\n"; }
// A(const A &) { cout << "Copy A\n"; }
//
// A(A &&) { cout << "Move A\n"; }
A(const A&&)= delete;
A(A&&)= delete;
};
A getA_unnamed() {
return A();
}
int main() {
auto a = getA_unnamed();
}

View File

@@ -0,0 +1,762 @@
## 0.回顾
前面一节编写了一个RAII的例子
```cpp
class shape_wrapper {
public:
explicit shape_wrapper(
shape* ptr = nullptr)
: ptr_(ptr) {}
~shape_wrapper()
{
delete ptr_;
}
shape* get() const { return ptr_; }
private:
shape* ptr_;
};
```
这个类可以完成智能指针的最基本的功能:对超出作用域的对象进行释放。但它缺了点东
西:
- 这个类只适用于 shape 类
- 该类对象的行为不够像指针
- 拷贝该类对象会引发程序行为
## 1.手写auto_ptr与scope_ptr
针对**"这个类只适用于 shape 类"**,我们想到了模板,于是改造为:
```cpp
template <typename T>
class smater_ptr {
public:
explicit smater_ptr(
T* ptr = nullptr)
: ptr_(ptr) {}
~smater_ptr()
{
delete ptr_;
}
T* get() const { return ptr_; }
private:
T* ptr_;
};
```
针对**"该类对象的行为不够像指针"**,我们想到了指针的基本操作有`*`,`->`,布尔表达式。
于是添加三个成员函数:
```cpp
template <typename T>
class smater_ptr {
public:
...
T& operator*() const { return *ptr_; }
T* operator->() const { return ptr_; }
operator bool() const { return ptr_; }
...
private:
T* ptr_;
};
```
针对**"拷贝该类对象会引发程序行为"**,我们想到了拷贝构造和赋值。
现考虑如下调用:
```cpp
smart_ptr<shape> ptr1{create_shape(shape_type::circle)};
smart_ptr<shape> ptr2{ptr1};
```
对于第二行,究竟应当让编译时发生错误,还是可以有一个更合理的行为?我们来逐一检查
一下各种可能性。
最简单的情况显然是禁止拷贝。我们可以使用下面的代码:
```cpp
template <typename T>
class smart_ptr {
smart_ptr(const smart_ptr&)
= delete;
smart_ptr& operator=(const smart_ptr&)
= delete;
};
```
当然也可以设为private。
禁用这两个函数非常简单,但却解决了一种可能出错的情况。否则,`smart_ptr<shape> ptr2{ptr1};` 在编译时不会出错,但在运行时却会有未定义行为——**由于会对同一内存释放两次,通常情况下会导致程序崩溃。**
我们是不是可以考虑在拷贝智能指针时把对象拷贝一份?不行,通常人们不会这么用,因为使用智能指针的目的就是要减少对象的拷贝啊。何况,虽然我们的指针类型是 shape但实际指向的却应该是 circle 或 triangle 之类的对象。在 C++ 里没有像 Java 的clone 方法这样的约定;**一般而言,并没有通用的方法可以通过基类的指针来构造出一个子类的对象来。**
那关键点就来了,**所有权!**,我们可以拷贝时转移指针的所有权!下面实现便是`auto_ptr`的核心实现:
```cpp
template<typename T>
class auto_ptr {
public:
explicit auto_ptr(
T *ptr = nullptr) noexcept
: ptr_(ptr) {}
~auto_ptr() noexcept {
delete ptr_;
}
// 返回值为T&,允许*ptr=10操作
T &operator*() const noexcept { return *ptr_; }
T *operator->() const noexcept { return ptr_; }
operator bool() const noexcept { return ptr_; }
T *get() const noexcept { return ptr_; }
// 拷贝构造,被复制放释放原来指针的所有权,交给复制方
auto_ptr(auto_ptr &other) noexcept {
ptr_ = other.release();
}
// copy and swap
auto_ptr &operator=(auto_ptr &rhs) noexcept {
// auto_ptr tmp(rhs.release());
// tmp.swap(*this);
// s上述两行等价于下面一行
auto_ptr(rhs.release()).swap(*this);
return *this;
}
// 原来的指针释放所有权
T *release() noexcept {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(auto_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_); // 转移指针所有权
}
private:
T *ptr_;
};
template<typename T>
void swap(auto_ptr<T> &lhs, auto_ptr<T> &rhs) noexcept {
lhs.swap(rhs);
}
int main() {
auto_ptr<shape> ptr1{create_shape(shape_type::circle)};
auto_ptr<shape> ptr2{ptr1};
if (ptr1.get() == nullptr && ptr2.get())
cout << "拷贝构造ptr1释放了所有权,ptr2获得了所有权" << endl;
ptr1 = ptr1;
auto_ptr<shape> ptr3{create_shape(shape_type::rectangle)};
ptr1 = ptr3;
if (ptr3.get() == nullptr && ptr1.get())
cout << "赋值操作:始终只有一个对象管理一个区块!ptr3释放了所有权,ptr1获得了所有权" << endl;
}
```
上述通过copy-swap技术完成了避免自我赋值与保证了强异常安全
如果你觉得这个实现还不错的话,那恭喜你,你达到了 C++ 委员会在 1998 年时的水平:上面给出的语义本质上就是 C++98 的 auto_ptr 的定义。如果你觉得这个实现很别扭的话,也恭喜你,因为 C++ 委员会也是这么觉得的:**auto_ptr 在 C++17 时已经被正式从C++ 标准里删除了**。
上面会导致什么问题呢?
看一下输出结果:
```cpp
shape
circle
拷贝构造:ptr1释放了所有权,ptr2获得了所有权
shape
rectangle
赋值操作:始终只有一个对象管理一个区块!ptr3释放了所有权,ptr1获得了所有权
```
shape与circle实在create_shape时候输出的我们重点关注最后一句话发现了一个很大的问题**它的行为会让程序员非常容易犯错。一不小心把它传递给另外一个 auto_ptr你就不再拥有这个对象了。**
上述拷贝构造与拷贝赋值分别如下面两张图所示:
针对这个问题在C++11标准出来之前C++98标准中都一直只有一个智能指针auto_ptr我们知道这是一个失败的设计。它的本质是**管理权的转移**这有许多问题。而这时就有一群人开始扩展C++标准库的关于智能指针的部分他们组成了boost社区他们负责boost库的开发和维护。其目的是为C++程序员提供免费的、同行审查的、可移植的程序库。boost库可以和C++标准库完美的共同工作,并且为其提供扩展功能。现在的**C++11标准库**的智能指针很大程度上“借鉴”了boost库。
boost::scoped_ptr 属于 boost 库,定义在 namespace boost 中,包含头文件`#include<boost/smart_ptr.hpp> `可以使用。scoped_ptr 跟 auto_ptr 一样可以方便的管理单个堆内存对象特别的是scoped_ptr 独享所有权避免了auto_ptr恼人的几个问题。
<u>scoped_ptr是一种简单粗暴的设计它本质就是**防拷贝**,避免出现管理权的转移。</u>这是它的最大特点所以他的拷贝构造函数和赋值运算符重载函数都只是声明而不定义而且为了防止有的人在类外定义所以将函数声明为private。但这也是它最大的问题所在就是不能赋值拷贝也就是说功能不全。但是这种设计比较高效、简洁。没有 release() 函数不会导致先前的内存泄露问题。下面我也将模拟实现scoped_ptr的管理机制(实际上就是前面提到的禁止拷贝)
```cpp
template<class T>
class scoped_ptr // noncopyable
{
public:
explicit scoped_ptr(T *ptr = 0) noexcept : ptr_(ptr) {
}
~scoped_ptr() noexcept {
delete ptr_;
}
void reset(T *p = 0) noexcept {
scoped_ptr(p).swap(*this);
}
T &operator*() const noexcept {
return *ptr_;
}
T *operator->() const noexcept {
return ptr_;
}
T *get() const noexcept {
return ptr_;
}
void swap(scoped_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_);
}
private:
T *ptr_;
scoped_ptr(scoped_ptr const &);
scoped_ptr &operator=(scoped_ptr const &);
};
template<typename T>
void swap(scoped_ptr<T> &lhs, scoped_ptr<T> &rhs) noexcept {
lhs.swap(rhs);
}
```
scoped_ptr特点总结
1与auto_ptr类似采用栈上的指针去管理堆上的内容从而使得堆上的对象随着栈上对象销毁时自动删除
2scoped_ptr有着更严格的使用限制——不能拷贝这也意味着scoped_ptr不能转换其所有权所以它管理的对象不能作为**函数的返回值**,对象生命周期仅仅局限于一定区间(该指针所在的{}区间而std::auto_ptr可以
3由于防拷贝的特性使其管理的对象**不能共享所有权**这与std::auto_ptr类似这一特点使该指针简单易用但也造成了功能的薄弱。
## 2.手写unique_ptr之子类向基类转换
在上述auto_ptr基础上我们把拷贝构造与拷贝赋值改为移动构造与移动赋值。
```cpp
template<typename T>
class unique_ptr {
public:
explicit unique_ptr(
T *ptr = nullptr) noexcept
: ptr_(ptr) {}
~unique_ptr() noexcept {
delete ptr_;
}
T &operator*() const noexcept { return *ptr_; }
T *operator->() const noexcept { return ptr_; }
operator bool() const noexcept { return ptr_; }
T *get() const noexcept { return ptr_; }
unique_ptr(unique_ptr &&other) noexcept {
ptr_ = other.release();
}
// copy and swap 始终只有一个对象有管理这块空间的权限
unique_ptr &operator=(unique_ptr rhs) noexcept {
rhs.swap(*this);
return *this;
}
// 原来的指针释放所有权
T *release() noexcept {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(unique_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_); // 转移指针所有权
}
private:
T *ptr_;
};
template<typename T>
void swap(unique_ptr<T> &lhs, unique_ptr<T> &rhs) {
lhs.swap(rhs);
}
```
调用:
```cpp
int main() {
unique_ptr<shape> ptr1{create_shape(shape_type::circle)};
// unique_ptr<shape> ptr2{ptr1}; // error
unique_ptr<shape> ptr2{std::move(ptr1)}; // ok
unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};
// ptr1 = ptr3; // error
ptr3 = std::move(ptr1); // ok
}
```
把拷贝构造函数中的参数类型 unique_ptr& 改成了 unique_ptr&&;现在它成了移动构造函数。
把赋值函数中的参数类型 unique_ptr& 改成了 unique_ptr在构造参数时直接生成新的智能指针从而不再需要在函数体中构造临时对象。现在赋值函数的行为是移动还是拷贝完全依赖于构造参数时走的是移动构造还是拷贝构造。
最后,一个` circle*` 是可以隐式转换成 `shape*`的,但上面的 `unique_ptr<circle>` 却无法自动转换成 `unique_ptr<shape>`
现在我们考虑两种情况:
**1第一种当我们只是在原先的移动构造上面添加`template <typename U>`,此时情况是移动构造变为带模板的移动构造,可以进行子类向基类转换,但是与移动构造相关的,则调用的是默认移动构造,除非是子类向基类转换,才调用带模板的移动构造。**
```cpp
template <typename U>
unique_ptr(unique_ptr<U> &&other) noexcept {
ptr_ = other.release();
}
```
**六个特殊的成员函数其生成规则如下:**
- **默认构造函数生成规则和C++98一样在用户没有声明自定义的构造函数的时候并且编译期需要的时候生成。**
- **析构函数生成规则和C++98一样在C++11中有点不同的是析构函数默认是noexcept。**
- **拷贝构造函数用户自定义了移动操作会导致不生成默认的拷贝构造函数其它和C++98的行为一致。**
- **拷贝赋值操作符用户自定义了移动操作会导致不生成默认的拷贝赋值操作其它和C++98的行为一致。**
- **移动构造函数和移动赋值操作符,仅仅在没有用户自定义的拷贝操作,移动操作和析构操作的时候才会生成。**
根据《Effective Modern C++》Item17 P115页提到当类中含有特殊成员函数变为模板特殊成员函数的时候,此时不满足上述生成规则也就是针对当前例子来说编译器会默认生成拷贝构造所以此时上述main调用里面为error的都可以正常运行
```cpp
int main() {
unique_ptr<shape> ptr1{create_shape(shape_type::circle)};
unique_ptr<shape> ptr2{ptr1}; // 由于带模板的移动构造函数引发编译器会默认生成拷贝构造
if (ptr1.get() != nullptr) // bitwise copy 此时ptr1不为NULL
ptr2.get()->print();
unique_ptr<shape> ptr2_2{std::move(ptr1)}; // 调用的是默认的移动构造,而不是带模板的移动构造 bitwise move
if (ptr2_2.get() != nullptr && ptr1.get() != nullptr) // ptr1 不为空
ptr2_2.get()->print();
unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};
ptr1 = ptr3; // ok 根据形参先调用默认拷贝,再调用拷贝赋值
ptr3 = std::move(ptr1); // ok 根据形参先调用默认移动构造,而不是带参数的移动构造,再调用移动赋值
unique_ptr<shape> ptr4(std::move(new circle)); // ok 调用带模板的移动构造
}
```
调用与结果如上代码所示。
**2第二种移动构造与带模板的移动构造同时存在可以完成子类向基类的转换此时是满足上述生成规则此时不会生成拷贝函数**
```cpp
int main() {
unique_ptr<shape> ptr1{create_shape(shape_type::circle)};
// unique_ptr<shape> ptr2{ptr1}; // error
unique_ptr<shape> ptr2_2{std::move(ptr1)}; // ok
if (ptr2_2.get() != nullptr && ptr1.get() == nullptr)
ptr2_2.get()->print();
unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};
// ptr1 = ptr3; // error
ptr3 = std::move(ptr1); // ok
// unique_ptr<circle> cl{create_shape(shape_type::circle)}; // error 因为create_shape返回的是shape 不能基类转子类
unique_ptr<circle> cl{new circle()};
unique_ptr<shape> ptr5(std::move(cl)); // ok unique<circle>转unique<circle>
}
```
**小结:**
1我们需要了解子类向基类的隐式转换通过将移动构造函数变为带模板的移动构造函数要明白两者共存情况与只有带模板的移动或者其他构造函数对编译器生成规则的影响上述代码此时还不能完成基类向子类的转换例如:`unique_ptr<circle>``unique_ptr<shape>`
2auto_ptr与unique_tr都是独占所有权每次只能被单个对象所拥有unique_ptr与auto_ptr不同的是使用移动语义来显示的编写。auto_ptr是可以说你随便赋值,但赋值完了之后原来的对象就不知不觉的报废.搞得你莫名其妙。而unique_ptr就干脆不让你可以随便去复制,赋值.如果实在想传个值就哪里,显式的说明内存转移std:move一下。然后这样传值完了之后,之前的对象也同样报废了.只不过整个move你让明显的知道这样操作后会导致之前的unique_ptr对象失效。scope_ptr则是直接不允许拷贝。由于防拷贝的特性使其管理的对象**不能共享所有权**。
## 3.shared_ptr之引用计数
unique_ptr 算是一种较为安全的智能指针了。但是,一个对象只能被单个 unique_ptr所拥有这显然不能满足所有使用场合的需求。一种常见的情况是多个智能指针同时拥有一个对象当它们全部都失效时这个对象也同时会被删除。这也就是 shared_ptr 了。
两者区别如下:
多个shared_ptr不仅共享一个对象同时还得共享同一个计数。当最后一个指向对象(和共享计数)的shared_ptr析构时它需要删除对象和共享计数。
首先需要一个共享计数的实现:
```cpp
class shared_count {
public:
shared_count() : count_(1) {
}
// 增加计数
void add_count() {
++count_;
}
// 减少计数
long reduce_count() {
return --count_;
}
// 获取当前计数
long get_count() const {
return count_;
}
private:
long count_;
};
```
接下来实现引用计数智能指针:
构造与析构、swap实现如下所示
```cpp
template<typename T>
class shared_ptr {
public:
explicit shared_ptr(
T *ptr = nullptr) noexcept
: ptr_(ptr) {
if (ptr) {
shared_count_ = new shared_count();
}
}
~shared_ptr() noexcept {
// 最后一个shared_ptr再去删除对象与共享计数
// ptr_不为空且此时共享计数减为0的时候,再去删除
if(ptr_&&!shared_count_->reduce_count()) {
delete ptr_;
delete shared_count_;
}
}
void swap(shared_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_);
swap(shared_count_,rhs.shared_count_);
}
private:
T *ptr_;
shared_count *shared_count_;
};
template<typename T>
void swap(shared_ptr<T> &lhs, shared_ptr<T> &rhs) noexcept {
lhs.swap(rhs);
}
```
之前的赋值函数,编译器可以根据调用来决定是调拷贝构造还是移动构函数,所以不变:
```cpp
// copy and swap 始终只有一个对象有管理这块空间的权限
shared_ptr &operator=(shared_ptr rhs) noexcept {
rhs.swap(*this);
return *this;
}
```
拷贝构造与移动构造需要改变:
除复制指针之外,对于拷贝构造的情况,我们需要在指针非空时把引用数加一,并复制共享
计数的指针。对于移动构造的情况,我们不需要调整引用数,直接把 other.ptr_ 置为
空,认为 other 不再指向该共享对象即可
实现如下所示:
```cpp
template<typename U>
shared_ptr(const shared_ptr<T> &other) noexcept {
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_->add_count();
shared_count_ = other.shared_count_;
}
}
template<typename U>
shared_ptr(shared_ptr<U> &&other) noexcept {
ptr_ = other.ptr_;
if (ptr_) {
shared_count_ = other.shared_count_;
other.shared_count_ = nullptr;
}
}
```
当运行的时候,报错:
```
circle* shared_ptr<circle>::ptr_ is private
```
错误原因是**模板的各个实例间并不天然就有 friend 关系**,因而不能互访私有成员 `ptr_ ``shared_count_`。我们需要在 smart_ptr 的定义中显式声明:
```cpp
template<typename U>
friend class shared_ptr;
```
此外在当前引用计数实现中我们应该删除release释放所有权函数编写一个返回引用计数值的函数。
```cpp
long use_count() const noexcept {
if (ptr_) {
return shared_count_->get_count();
} else {
return 0;
}
}
```
调用:
```cpp
shared_ptr<circle> ptr1(new circle());
cout << "use count of ptr1 is " << ptr1.use_count() << endl;
shared_ptr<shape> ptr2, ptr3;
cout << "use count of ptr2 was " << ptr2.use_count() << endl;
ptr2 = ptr1; // shared_ptr<circle>隐式转换shared_ptr<shape> 调用带模板的拷贝构造
// cout<<"======="<<endl;
// ptr3 = ptr2; // 调用的是编译器生成的默认拷贝构造 所以引用计数不会增加 ptr3=ptr2
// cout<<"======="<<endl;
ptr3 = ptr1;
cout << "此时3个shared_ptr指向同一个资源" << endl;
cout << "use count of ptr1 is now " << ptr1.use_count() << endl;
cout << "use count of ptr2 is now " << ptr2.use_count() << endl;
cout << "use count of ptr3 is now " << ptr3.use_count() << endl;
if (ptr1)
cout << "ptr1 is not empty" << endl;
// 会先调用赋值函数,由编译器决定调用的是拷贝构造还是移动构造,造出一个新的临时对象出来,临时对象会在跳出作用域后被析构掉。
// 在析构函数中,会先判断该临时对象的是否指向资源,如果没有,析构结束。否则,对引用计数减1,判断引用计数是否为0,如果为0,删除共享引用计数指针,否则不操作。
cout << "此时2个shared_ptr指向同一个资源" << endl;
ptr2 = std::move(ptr1);
if (!ptr1 && ptr2) { // 调用的是bool重载操作符
cout << "ptr1 move to ptr2" << endl;
cout << "use count of ptr1 is now " << ptr1.use_count() << endl;
cout << "use count of ptr2 is now " << ptr2.use_count() << endl;
cout << "use count of ptr3 is now " << ptr3.use_count() << endl;
}
```
输出:
```cpp
shape
circle
use count of ptr1 is 1
use count of ptr2 was 0
此时3shared_ptr指向同一个资源
use count of ptr1 is now 3
use count of ptr2 is now 3
use count of ptr3 is now 3
ptr1 is not empty
此时2shared_ptr指向同一个资源
ptr1 move to ptr2
use count of ptr1 is now 0
use count of ptr2 is now 2
use count of ptr3 is now 2
~circle
~shape
```
有几点注意事项:
- 上述代码没有考虑线程安全性,这里只是简化版
- =赋值重载函数不加`&`,编译器决定调用拷贝构造还是移动构造,来造出一个临时对象出来。
- 根据前面提到的,当类中特殊函数变为带模板的函数,编译器仍然会生成默认拷贝构造与默认移动构造。
针对第一点:例如:`ptr2 = std::move(ptr1);`
会先调用赋值函数,由编译器决定调用的是拷贝构造还是移动构造,造出一个新的临时对象出来,临时对象会在跳出作用域后被析构掉。在析构函数中,会先判断该临时对象的是否指向资源,如果没有,析构结束。否则,对引用计数减1,判断引用计数是否为0,如果为0,删除共享引用计数指针,否则不操作。
针对第二点:
```cpp
shared_ptr<shape> ptr2, ptr3;
ptr3 = ptr2; // 调用的是编译器生成的默认拷贝构造 所以引用计数不会增加
```
两者都是一种类型,所以在调用赋值操作后,不会调用带模板的拷贝构造来创建临时变量,而是调用编译器生成的默认拷贝构造,所以此时引用计数不会增加。
## 4.指针类型转换
对应于 C++ 里的不同的类型强制转:
- dynamic_cast
- static_cast
- const_cast
- reinterpret_cast
### 4.1 dynamic_cast
在上述`unique_ptr`处实现了子类向基类的转换,但是却没有实现基类向子类的转换,例如::`unique_ptr<circle>``unique_ptr<shape>`
实现这种,需要使用`dynamic_cast`,实现如下:
首先为了实现这些转换,我们需要添加构造函数,允许在对智能指针内部的指针对象赋值时,使用一个现有的智能指针的共享计数。
```cpp
// 实现强制类型转换需要的构造函数
template<typename U>
shared_ptr(const shared_ptr<U> &other, T *ptr) noexcept {
ptr_ = ptr;
if (ptr_) {
other.shared_count_->add_count();
shared_count_ = other.shared_count_;
}
}
```
其次,就是实现转换函数:
```cpp
template<typename T, typename U>
shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U> &other) noexcept {
T *ptr = dynamic_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
```
调用:
```cpp
// shape* -> circle* 使用dynamic_cast转换后,指针为空.此时资源还是被dptr2拥有,dptr1为0
shared_ptr<shape> dptr2(new shape);
shared_ptr<circle> dptr1 = dynamic_pointer_cast<circle>(dptr2); // 基类转子类
cout << "use count of dptr1 is now " << dptr1.use_count() << endl; // 0
cout << "use count of dptr2 is now " << dptr2.use_count() << endl; // 1
// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2
shared_ptr<shape> dptr3(new circle);
// shared_ptr<circle> dptr3(new circle); // 上面或者当前行,后面输出一样!
shared_ptr<circle> dptr1_1 = dynamic_pointer_cast<circle>(dptr3); // 基类转子类
cout << "use count of dptr1_1 is now " << dptr1_1.use_count() << endl; // 2
cout << "use count of dptr3 is now " << dptr3.use_count() << endl; // 2
// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2
shared_ptr<circle> dptr3_1(new circle);
shared_ptr<shape> dptr2_1 = dynamic_pointer_cast<shape>(dptr3_1); // 子类转基类 上行转换,安全!
cout << "use count of dptr2_1 is now " << dptr2_1.use_count() << endl; // 2
cout << "use count of dptr3_1 is now " << dptr3_1.use_count() << endl; // 2
```
dynamic_cast主要用于类层次间的上行转换和下行转换还可以用于类之间的交叉转换。在类层次间进行上行转换时dynamic_cast和static_cast的效果是一样的在进行下行转换时dynamic_cast具有类型检查的功能比static_cast更安全。在多态类型之间的转换主要使用dynamic_cast因为类型提供了运行时信息。
1下行转换基类转换为子类例如智能指针转换类似于`shape*` 转换为`circle*` 使用dynamic_cast转换后,指针为空.此时资源还是被dptr2拥有,dptr1为0。比static_cast安全。
2平行转换指向一致的相互转换例如智能指针转换类似于`circle*`转换为`circle*`。此时引用计数为两者共享。
3上行转换子类转基类例如智能指针转换类似于`circle*`转换为`shape*`此时引用技术为两者共享。等价于static_cast。
### 4.2 static_cast
同样,编写如下:
```cpp
template<typename T, typename U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U> &other) noexcept {
T *ptr = static_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
```
调用:
```
// shape* -> circle* 使用static_cast转换后,指针为空 与dynamic_cast相比,不安全
shared_ptr<shape> sptr2(new shape);
shared_ptr<circle> sptr1 = static_pointer_cast<circle>(sptr2); // 基类转子类
cout << "use count of sptr1 is now " << dptr1.use_count() << endl; // 0
cout << "use count of sptr2 is now " << dptr2.use_count() << endl; // 1
// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2
shared_ptr<shape> sptr3(new circle);
// shared_ptr<circle> sptr3(new circle); // 上面或者当前行,后面输出一样!
shared_ptr<circle> sptr1_1 = static_pointer_cast<circle>(sptr3); // 基类转子类
cout << "use count of sptr1_1 is now " << sptr1_1.use_count() << endl; // 2
cout << "use count of sptr3 is now " << sptr3.use_count() << endl; // 2
// circle* -> circle* 使用static_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2 等价于dynamic_cast
shared_ptr<circle> sptr3_1(new circle);
shared_ptr<shape> sptr2_1 = static_pointer_cast<shape>(sptr3_1); // 子类转基类 上行转换,安全!
cout << "use count of sptr2_1 is now " << sptr2_1.use_count() << endl; // 2
cout << "use count of sptr3_1 is now " << sptr3_1.use_count() << endl; // 2
```
输出结果同上dynamic_cast不同之处在下行转换的时候(基类转子类),是不安全的!
### 4.3 const_cast
去掉const属性
```cpp
template<typename T, typename U>
shared_ptr<T> const_pointer_cast(
const shared_ptr<U> &other) noexcept {
T *ptr = const_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
```
调用:
```cpp
shared_ptr<circle> s = const_pointer_cast<circle>(shared_ptr<const circle>(new circle));
```
### 4.4 reinterpret_cast
例如想把一个指针转为整数就可以用reinterpret_cast。
```cpp
template<typename T, typename U>
shared_ptr<T> reinterpret_pointer_cast(
const shared_ptr<U> &other) noexcept {
T *ptr = reinterpret_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
```
调用:
```cpp
int a = reinterpret_pointer_cast<int>(s);
```

View File

@@ -0,0 +1,120 @@
//
// Created by light on 19-12-9.
//
#include "../RAII/shape.h"
template<typename T>
class auto_ptr {
public:
explicit auto_ptr(
T *ptr = nullptr) noexcept
: ptr_(ptr) {}
~auto_ptr() noexcept {
delete ptr_;
}
T &operator*() const noexcept { return *ptr_; }
T *operator->() const noexcept { return ptr_; }
operator bool() const noexcept { return ptr_; }
T *get() const noexcept { return ptr_; }
// 拷贝构造,被复制放释放原来指针的所有权,交给复制方 始终只有一个对象管理一块空间
auto_ptr(auto_ptr &other) noexcept {
ptr_ = other.release();
}
// copy and swap 始终只有一个对象有管理这块空间的权限
auto_ptr &operator=(auto_ptr &rhs) noexcept {
// auto_ptr tmp(rhs.release());
// tmp.swap(*this);
// s上述两行等价于下面一行
auto_ptr(rhs.release()).swap(*this);
return *this;
}
// 原来的指针释放所有权
T *release() noexcept {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(auto_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_); // 转移指针所有权
}
private:
T *ptr_;
};
template<typename T>
void swap(auto_ptr<T> &lhs, auto_ptr<T> &rhs) noexcept {
lhs.swap(rhs);
}
template<class T>
class scoped_ptr // noncopyable
{
public:
explicit scoped_ptr(T *ptr = 0) noexcept : ptr_(ptr) {
}
~scoped_ptr() noexcept {
delete ptr_;
}
void reset(T *p = 0) noexcept {
scoped_ptr(p).swap(*this);
}
T &operator*() const noexcept {
return *ptr_;
}
T *operator->() const noexcept {
return ptr_;
}
T *get() const noexcept {
return ptr_;
}
void swap(scoped_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_);
}
private:
T *ptr_;
scoped_ptr(scoped_ptr const &);
scoped_ptr &operator=(scoped_ptr const &);
};
template<typename T>
void swap(scoped_ptr<T> &lhs, scoped_ptr<T> &rhs) noexcept {
lhs.swap(rhs);
}
int main() {
auto_ptr<shape> ptr1{create_shape(shape_type::circle)};
auto_ptr<shape> ptr2{ptr1};
if (ptr1.get() == nullptr && ptr2.get())
cout << "拷贝构造ptr1释放了所有权,ptr2获得了所有权" << endl;
ptr1 = ptr1;
auto_ptr<shape> ptr3{create_shape(shape_type::rectangle)};
ptr1 = ptr3;
if (ptr3.get() == nullptr && ptr1.get())
cout << "赋值操作:始终只有一个对象管理一个区块!ptr3释放了所有权,ptr1获得了所有权" << endl;
scoped_ptr<shape> sptr1{create_shape(shape_type::circle)};
// scoped_ptr<shape> sptr2{sptr1}; // error 不可拷贝
}

View File

@@ -0,0 +1,232 @@
//
// Created by light on 19-12-12.
//
#include "../RAII/shape.h"
class shared_count {
public:
shared_count() : count_(1) {
}
// 增加计数
void add_count() {
++count_;
}
// 减少计数
long reduce_count() {
return --count_;
}
// 获取当前计数
long get_count() const {
return count_;
}
private:
long count_;
};
template<typename T>
class shared_ptr {
public:
explicit shared_ptr(
T *ptr = nullptr) noexcept
: ptr_(ptr) {
if (ptr) {
shared_count_ = new shared_count();
}
}
// 实现强制类型转换需要的构造函数
template<typename U>
shared_ptr(const shared_ptr<U> &other, T *ptr) noexcept {
ptr_ = ptr;
if (ptr_) {
other.shared_count_->add_count();
shared_count_ = other.shared_count_;
}
}
~shared_ptr() noexcept {
// 最后一个shared_ptr再去删除对象与共享计数
// ptr_不为空且此时共享计数减为0的时候,再去删除
if (ptr_ && !shared_count_->reduce_count()) {
delete ptr_;
delete shared_count_;
}
}
T &operator*() const noexcept { return *ptr_; }
T *operator->() const noexcept { return ptr_; }
operator bool() const noexcept { return ptr_; }
T *get() const noexcept { return ptr_; }
// 带模板的拷贝与移动构造函数 模板的各个实例间并不天然就有 friend 关系,因而不能互访私有成员 ptr_ 和 shared_count_。
// 需要下面显示声明
template<typename U>
friend
class shared_ptr;
template<typename U>
shared_ptr(const shared_ptr<U> &other) noexcept {
// cout << "调用了带模板的拷贝构造!" << endl;
ptr_ = other.ptr_;
if (ptr_) {
other.shared_count_
->add_count();
shared_count_ =
other.shared_count_;
}
}
template<typename U>
shared_ptr(shared_ptr<U> &&other) noexcept {
// cout << "调用了带模板的移动构造!" << endl;
ptr_ = other.ptr_;
if (ptr_) {
shared_count_ =
other.shared_count_;
other.ptr_ = nullptr;
other.shared_count_ = nullptr;
}
}
// copy and swap 始终只有一个对象有管理这块空间的权限
shared_ptr &operator=(shared_ptr rhs) noexcept {
rhs.swap(*this);
return *this;
}
void swap(shared_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_);
swap(shared_count_, rhs.shared_count_);
}
long use_count() const noexcept {
if (ptr_) {
return shared_count_->get_count();
} else {
return 0;
}
}
private:
T *ptr_;
shared_count *shared_count_;
};
template<typename T>
void swap(shared_ptr<T> &lhs, shared_ptr<T> &rhs) noexcept {
lhs.swap(rhs);
}
template<typename T, typename U>
shared_ptr<T> dynamic_pointer_cast(const shared_ptr<U> &other) noexcept {
T *ptr = dynamic_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
template<typename T, typename U>
shared_ptr<T> static_pointer_cast(const shared_ptr<U> &other) noexcept {
T *ptr = static_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
template<typename T, typename U>
shared_ptr<T> const_pointer_cast(
const shared_ptr<U> &other) noexcept {
T *ptr = const_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
template<typename T, typename U>
shared_ptr<T> reinterpret_pointer_cast(
const shared_ptr<U> &other) noexcept {
T *ptr = reinterpret_cast<T *>(other.get());
return shared_ptr<T>(other, ptr);
}
int main() {
shared_ptr<circle> ptr1(new circle());
cout << "use count of ptr1 is " << ptr1.use_count() << endl;
shared_ptr<shape> ptr2, ptr3;
cout << "use count of ptr2 was " << ptr2.use_count() << endl;
ptr2 = ptr1; // shared_ptr<circle>隐式转换shared_ptr<shape> 调用带模板的拷贝构造
// cout<<"======="<<endl;
// ptr3 = ptr2; // 调用的是编译器生成的默认拷贝构造 所以引用计数不会增加 ptr3=ptr2
// cout<<"======="<<endl;
ptr3 = ptr1;
cout << "此时3个shared_ptr指向同一个资源" << endl;
cout << "use count of ptr1 is now " << ptr1.use_count() << endl;
cout << "use count of ptr2 is now " << ptr2.use_count() << endl;
cout << "use count of ptr3 is now " << ptr3.use_count() << endl;
if (ptr1)
cout << "ptr1 is not empty" << endl;
// 会先调用赋值函数,由编译器决定调用的是拷贝构造还是移动构造,造出一个新的临时对象出来,临时对象会在跳出作用域后被析构掉。
// 在析构函数中,会先判断该临时对象的是否指向资源,如果没有,析构结束。否则,对引用计数减1,判断引用计数是否为0,如果为0,删除共享引用计数指针,否则不操作。
cout << "此时2个shared_ptr指向同一个资源" << endl;
ptr2 = std::move(ptr1);
if (!ptr1 && ptr2) { // 调用的是bool重载操作符
cout << "ptr1 move to ptr2" << endl;
cout << "use count of ptr1 is now " << ptr1.use_count() << endl;
cout << "use count of ptr2 is now " << ptr2.use_count() << endl;
cout << "use count of ptr3 is now " << ptr3.use_count() << endl;
}
// shape* -> circle* 使用dynamic_cast转换后,指针为空.此时资源还是被dptr2拥有,dptr1为0
shared_ptr<shape> dptr2(new shape);
shared_ptr<circle> dptr1 = dynamic_pointer_cast<circle>(dptr2); // 基类转子类
cout << "use count of dptr1 is now " << dptr1.use_count() << endl; // 0
cout << "use count of dptr2 is now " << dptr2.use_count() << endl; // 1
// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2
shared_ptr<shape> dptr3(new circle);
// shared_ptr<circle> dptr3(new circle); // 上面或者当前行,后面输出一样!
shared_ptr<circle> dptr1_1 = dynamic_pointer_cast<circle>(dptr3); // 基类转子类
cout << "use count of dptr1_1 is now " << dptr1_1.use_count() << endl; // 2
cout << "use count of dptr3 is now " << dptr3.use_count() << endl; // 2
// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2
shared_ptr<circle> dptr3_1(new circle);
shared_ptr<shape> dptr2_1 = dynamic_pointer_cast<shape>(dptr3_1); // 子类转基类 上行转换,安全!
cout << "use count of dptr2_1 is now " << dptr2_1.use_count() << endl; // 2
cout << "use count of dptr3_1 is now " << dptr3_1.use_count() << endl; // 2
// shape* -> circle* 使用static_cast转换后,指针为空 与dynamic_cast相比,不安全
shared_ptr<shape> sptr2(new shape);
shared_ptr<circle> sptr1 = static_pointer_cast<circle>(sptr2); // 基类转子类
cout << "use count of sptr1 is now " << dptr1.use_count() << endl; // 0
cout << "use count of sptr2 is now " << dptr2.use_count() << endl; // 1
// circle* -> circle* 使用dynamic_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2
shared_ptr<shape> sptr3(new circle);
// shared_ptr<circle> sptr3(new circle); // 上面或者当前行,后面输出一样!
shared_ptr<circle> sptr1_1 = static_pointer_cast<circle>(sptr3); // 基类转子类
cout << "use count of sptr1_1 is now " << sptr1_1.use_count() << endl; // 2
cout << "use count of sptr3 is now " << sptr3.use_count() << endl; // 2
// circle* -> circle* 使用static_cast转换后,指针不为空,此时资源被两者共同使用,引用计数为2 等价于dynamic_cast
shared_ptr<circle> sptr3_1(new circle);
shared_ptr<shape> sptr2_1 = static_pointer_cast<shape>(sptr3_1); // 子类转基类 上行转换,安全!
cout << "use count of sptr2_1 is now " << sptr2_1.use_count() << endl; // 2
cout << "use count of sptr3_1 is now " << sptr3_1.use_count() << endl; // 2
shared_ptr<const int> constV(new int);
shared_ptr<int> s = const_pointer_cast<int>(constV);
*s =10;
int a = reinterpret_pointer_cast<int>(s);
cout<<a<<endl;
}

View File

@@ -0,0 +1,79 @@
//
// Created by light on 19-12-12.
//
#include "../RAII/shape.h"
template<typename T>
class unique_ptr {
public:
explicit unique_ptr(
T *ptr = nullptr) noexcept
: ptr_(ptr) {}
~unique_ptr() noexcept {
delete ptr_;
}
T &operator*() const noexcept { return *ptr_; }
T *operator->() const noexcept { return ptr_; }
operator bool() const noexcept { return ptr_; }
T *get() const noexcept { return ptr_; }
unique_ptr(unique_ptr &&other) noexcept {
cout << "move ctor" << endl;
ptr_ = other.release();
}
template<typename U>
unique_ptr(unique_ptr<U> &&other) noexcept {
cout << "U move ctor" << endl;
ptr_ = other.release();
}
// copy and swap 始终只有一个对象有管理这块空间的权限
unique_ptr &operator=(unique_ptr rhs) noexcept {
rhs.swap(*this);
return *this;
}
// 原来的指针释放所有权
T *release() noexcept {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(unique_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_); // 转移指针所有权
}
private:
T *ptr_;
};
template<typename T>
void swap(unique_ptr<T> &lhs, unique_ptr<T> &rhs) {
lhs.swap(rhs);
}
int main() {
unique_ptr<shape> ptr1{create_shape(shape_type::circle)};
// unique_ptr<shape> ptr2{ptr1}; // error
unique_ptr<shape> ptr2_2{std::move(ptr1)}; // ok
if (ptr2_2.get() != nullptr && ptr1.get() == nullptr)
ptr2_2.get()->print();
unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};
// ptr1 = ptr3; // error
ptr3 = std::move(ptr1); // ok
// unique_ptr<circle> cl{create_shape(shape_type::circle)}; // error 因为create_shape返回的是shape 不能基类转子类
unique_ptr<circle> cl{new circle()};
unique_ptr<shape> ptr5(std::move(cl)); // ok unique<circle>转unique<circle>
}

View File

@@ -0,0 +1,72 @@
//
// Created by light on 19-12-12.
//
#include "../RAII/shape.h"
template<typename T>
class unique_ptr {
public:
explicit unique_ptr(
T *ptr = nullptr) noexcept
: ptr_(ptr) {}
~unique_ptr() noexcept {
delete ptr_;
}
T &operator*() const noexcept { return *ptr_; }
T *operator->() const noexcept { return ptr_; }
operator bool() const noexcept { return ptr_; }
T *get() const noexcept { return ptr_; }
template<typename U>
unique_ptr(unique_ptr<U> &&other) noexcept {
cout << "U move ctor" << endl;
ptr_ = other.release();
}
// copy and swap 始终只有一个对象有管理这块空间的权限
unique_ptr &operator=(unique_ptr rhs) noexcept {
rhs.swap(*this);
return *this;
}
// 原来的指针释放所有权
T *release() noexcept {
T *ptr = ptr_;
ptr_ = nullptr;
return ptr;
}
void swap(unique_ptr &rhs) noexcept {
using std::swap;
swap(ptr_, rhs.ptr_); // 转移指针所有权
}
private:
T *ptr_;
};
int main() {
unique_ptr<shape> ptr1{create_shape(shape_type::circle)};
unique_ptr<shape> ptr2{ptr1}; // 由于带模板的移动构造函数引发编译器会默认生成拷贝构造
if (ptr1.get() != nullptr) // bitwise copy 此时ptr1不为NULL
ptr2.get()->print();
unique_ptr<shape> ptr2_2{std::move(ptr1)}; // 调用的是默认的移动构造,而不是带模板的移动构造 bitwise move
if (ptr2_2.get() != nullptr && ptr1.get() != nullptr) // ptr1 不为空
ptr2_2.get()->print();
unique_ptr<shape> ptr3{create_shape(shape_type::rectangle)};
ptr1 = ptr3; // ok 根据形参先调用默认拷贝,再调用拷贝赋值
ptr3 = std::move(ptr1); // ok 根据形参先调用默认移动构造,而不是带参数的移动构造,再调用移动赋值
unique_ptr<shape> ptr4(std::move(new circle)); // ok 调用带模板的移动构造
}