实现原理,
代码示例是照着《深入应用C++11》抄的。
- 使用变长模板参数构造任意对象。
- 使用std::aligned_storage实现任意长度对象的内存对齐。
- 使用std::forward减少参数拷贝,实现右值引用的完美转发。
optional.hpp
#ifndef _OPTIONAL_HPP_
#define _OPTIONAL_HPP_
#include <type_traits>
#include <functional>
template <typename T>
class Optional {
using data_t = typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type;
public:
Optional() {}
Optional(const T& v) {
Create(v);
}
Optional(T&& v) {
Create(std::move(v));
}
~Optional() {
Destroy();
}
Optional(const Optional& other) {
if(other.IsInit()) {
Assign(other);
}
}
Optional(Optional&& other) {
if(other.IsInit()) {
Assign(std::move(other));
other.Destroy();
}
}
Optional& operator=(Optional&& other) {
Assign(std::move(other));
return *this;
}
Optional& operator=(const Optional& other) {
Assign(other);
return *this;
}
template <class ... Args>
void emplace(Args && ... args) {
Destroy();
Create(std::forward<Args>(args)...);
}
explicit operator bool() const {
return IsInit();
}
T& operator*() {
return *((T*)(&m_data));
}
T const& operator*() const {
if(IsInit()) {
return *((T*)(&m_data));
}
throw std::exception("解引用失败!");
}
bool operator == (const Optional<T>& rhs) const {
// 1. 两个对象的初始化状态不等, return false
// 2. 如果初始化状态相等,当前对象没有被初始化,说明两个对象都没有被初始化,那相等,return true
// 3. 状态相等,且都被初始化了,比较Optional对象指向的值
return (!bool(*this)) != (!rhs) ? false: (!bool(*this)) ? true: (*(*this)) == (*rhs);
}
bool operator < (const Optional<T>& rhs) const {
// 1. 如果右值没有初始化, 不可能小于空值,return false
// 2. 如果右值不为空,左值为空,肯定小于 return true
// 3. 左值,右值都不为空,比较值
return !rhs ? false: (!(bool(*this))) ? true: ((*(*this)) < (*rhs));
}
bool operator != (const Optional<T>& rhs) const {
return !(*this == (rhs));
}
bool IsInit() const {
return m_hasInit;
}
private:
template <typename... Args>
void Create(Args&&... args) {
new (&m_data) T(std::forward<Args>(args)...);
m_hasInit = true;
}
void Destroy() {
if(m_hasInit) {
m_hasInit = false;
// 调用T的析构函数释放资源
((T*)(&m_data))->~T();
}
}
void Assign(const Optional& other) {
if(other.IsInit()) {
Copy(other.m_data);
m_hasInit = true;
}else {
Destroy();
}
}
void Assign(Optional&& other) {
if(other.IsInit()) {
Move(std::move(other.m_data));
m_hasInit = true;
other.Destroy();
} else {
Destroy();
}
}
// 调用移动构造函数移动构造
void Move(data_t&& val) {
Destroy();
new (&m_data) T(std::move(*((T*)(&val))));
}
void Copy(const data_t& val) {
Destroy();
// 直接释放调对象后在原址上用引用值重新分配,会产生拷贝
new (&m_data) T(*((T*)(&val)));
}
private:
bool m_hasInit = false;
data_t m_data;
};
#endif
main.cpp测试代码
#include "optional.hpp"
#include <iostream>
#include <string>
using std::cout;
using std::endl;
using std::string;
#include <cstdlib>
struct MyStruct {
MyStruct() {}
MyStruct(int a, int b): m_a(a), m_b(b) {}
int m_a;
int m_b;
};
void testOp() {
Optional<string> a("ok");
Optional<string> b("cancel");
Optional<string> c("aa");
Optional<string> d = a;
cout << "d=" << *d << endl;
Optional<MyStruct> op;
op.emplace(1, 2);
MyStruct t;
if(op) {
t = *op;
}
cout << "t.m_a= " << t.m_a << " t.m_b= " << t.m_b << endl;
op.emplace(3, 4);
if(op) {
t = *op;
}
cout << "t.m_a= " << t.m_a << " t.m_b= " << t.m_b << endl;
}
int main(void) {
testOp();
cout << "请按任意键继续..." << endl;
getchar();
return 0;
}
Makefile文件代码
TAR=main
WORKSPACE_DIR=.
CC:=g++
.PHONY: build clear all
build:
$(CC) -std=c++11 $(WORKSPACE_DIR)/optional.hpp $(WORKSPACE_DIR)/main.cpp -g -o $(TAR)
all: clear build
clear:
rm -rf $(TAR)
测试程序的输出如下,
图片.png
网友评论