[英]How do i implement lazy initialization and caching in an immutable c++ object?
我很早以前就了解了“優雅對象”的原理(請參閱elegantobjects.org),它們很容易在C#中遵循,但是現在我在做一些C ++,不變性給我帶來了麻煩。 我正在尋找有關如何以不變的方式實現延遲初始化和緩存的最佳實踐。
延遲初始化和緩存的結合意味着,某些數據在構造之后必須存儲在對象內部,這需要某種可變的行為。 在C#中,如果類的所有字段都聲明為“只讀”,則對象是不可變的。 幸運的是,這是一個淺層的不變性,因此您的不變對象可以將可變對象封裝為只讀字段,並對其進行操作以實現可變行為,請參見下面的C#示例。
與C ++中C#的“只讀”最接近的事情是將字段聲明為“ const”。 但是,在C ++中,如果將其他可變對象存儲在const字段中,則無法像在C#中那樣直接操作它。 嘗試在C ++中重新創建C#示例會導致編譯時錯誤,請參見下面的第二個示例。
有一種解決方法:將每個數組轉換為void指針並返回到我需要的類型的指針(例如int或bool),然后將該指針視為數組,這樣我就可以繞過原始數組的const限定符。 但是,這很丑陋,似乎有些骯臟,並且使代碼的可讀性比我剛刪除const限定符時的可讀性差。
我確實希望在那里有那些const限定詞,它們是一種確保人們確信類確實是不可變的正式方法,而無需他們閱讀整個類的代碼。
我想要實現的C#示例:
using System;
public sealed class Program
{
public static void Main()
{
var test =
new CachedInt(
()=>5 // this lambda might aswell be a more expensive calculation, which would justify lazy initialization and caching
);
Console.WriteLine(test.Value());
}
}
public sealed class CachedInt
{
//note that these are all readonly, this is an immutable class
private readonly Func<int> source;
private readonly int[] cache;
private readonly bool[] hasCached;
public CachedInt(Func<int> source)
{
this.source = source;
this.cache = new int[1];
this.hasCached = new bool[1]{false};
}
public int Value()
{
if(!this.hasCached[0])
{
// manipulating mutable objects stored as readonly fields:
this.cache[0] = this.source.Invoke();
this.hasCached[0] = true;
}
return this.cache[0];
}
}
導致編譯時錯誤的C ++示例:
#include <iostream>
#include <functional>
class CachedInt final
{
private:
// all const, this is an immutable class
const std::function<int()> source;
const int cache[1];
const bool hasCached[1];
public:
CachedInt(std::function<int()> source) :
source(source),
cache{0},
hasCached{false}
{}
int Value()
{
if(!this->hasCached[0])
{
// the following two lines obviously don't work due to the const qualification
this->cache[0] = this->source();
this->hasCached[0] = true;
}
return this->cache[0];
}
};
int main()
{
CachedInt test([]()->int{return 5;});
std::cout << test.Value();
}
丑陋的解決方法:
#include <iostream>
#include <functional>
class CachedInt final
{
private:
// all const, this is an immutable class
const std::function<int()> source;
const int cache[1];
const bool hasCached[1];
public:
CachedInt(std::function<int()> source) :
source(source),
cache{0},
hasCached{false}
{}
int Value()
{
if(!this->hasCached[0])
{
// this works but it's ugly. there has to be a better way.
((int*)(void*)this->cache)[0] = this->source();
((bool*)(void*)this->hasCached)[0] = true;
}
return this->cache[0];
}
};
int main()
{
CachedInt test([]()->int{return 5;});
std::cout << test.Value();
}
嘗試編譯第二個示例時拋出的錯誤:
In member function 'int CachedInt::Value()':
24:28: error: assignment of read-only location '((CachedInt*)this)->CachedInt::cache[0]'
25:32: error: assignment of read-only location '((CachedInt*)this)->CachedInt::hasCached[0]'
這個錯誤不是問題,我知道為什么會拋出它,我只是為了完整性而添加它。
總而言之,我希望一個類進行延遲初始化並緩存結果,但我也希望該類是不可變的。 用c ++做到這一點的最優雅的方法是什么?
這是一條紅色的鯡魚:由於緩存是private
,因此它是否是const
無關緊要:您(該類的成員函數的設計者)可以完全控制更改其值的任何嘗試。
這是一個C ++實現,其中值由const
成員訪問
template<typename valueType>
struct cachedFunctionValue {
cachedFunctionValue(std::function<valueType()> &&f)
: func(f) {}
valueType get() const // constant access to function value
{
if(!cache.has_value())
cache = func(); // works because cache is mutable
return cache.value();
}
private:
const std::function<valueType()> func;
mutable std::optional<valueType> cache;
};
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.