如何在C ++中抽象延迟初始化?

编程入门 行业动态 更新时间:2024-10-09 08:32:18
本文介绍了如何在C ++中抽象延迟初始化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧! 问题描述

前几天在重构一些代码以提高性能时,我需要一个答案来创建延迟初始化的成员变量,但它也为非c ++ 11编译器提供了一个方便但可选的非lambda接口.

While refactoring some code for performance the other day, I needed an answer to creating member variables that are lazy initialized, but that also provides a convenient, though optional, non-lambda interface for non c++11 compilers.

这是我要抽象的典型的惰性实例化模式:

Here's the typical pattern for lazy instantiation that I want to abstract:

if( !bInitialized ) { value = doInitialization(); bInitialized = true; } return value;

我想要一些灵活性:

  • 允许进行显式初始化,如上面的示例
  • 提供对延迟的隐式访问,就好像它是基础数据类型一样
  • 处理未初始化的访问(抛出),以防万一我搞砸了显式初始化(​​例如,忘记分配值)
  • 还通过函数,函子和/或lambda支持真正的延迟初始化
  • 允许通过指向所包含值的指针进行手动初始化(例如,在调用Win32 API时)
  • 允许重新分配值;在大多数情况下,将懒惰视为基础数据类型.

我有要发布的代码作为答案,但会对其他方法感兴趣.您的答案不必满足所有这些要求;在某些用例中,更简单可能会更好...

I have code that I'm going to post as an answer, but would be interested in different approaches. Your answer need not satisfy all these requirements; simpler may be better for some use cases...

推荐答案

这是我的解决方案,包括基于Microsoft本机单元测试库构建的单元测试套件.

Here's my solution, including a unit test suite built on the Microsoft Native Unit Test Library.

它处理OP的需求-一个单一的Lazy类,它提供:

It handles the requirements of the OP - a single Lazy class that provides:

  • 通过函数回调,函子或lambda隐式首次使用"初始化
  • 或通过赋值进行显式初始化
  • 或通过指向内部数据结构的指针进行显式初始化
  • 如果尝试访问未初始化的延迟,则会引发异常;例如,表明您忘记了显式初始化懒惰,并且没有隐式初始化程序

加上

  • 它可以与没有默认构造函数的数据类型一起使用
  • 并通过可选的反初始化(关闭)回调处理惰性资源管理

首先,一个用法示例:

class MyClass { Lazy<int> m_test; Lazy<int> m_testViaInitializer; Lazy<int> m_testViaInitializationOfPointer; public: MyClass::MyClass() : m_testViaInitializer( intFactory ) { } int MyClass::lazy_ImplicitInitialization() { return m_testViaInitializer; } int MyClass::lazy_ExplicitInitialization() { if( !m_test.isInitialized() ) { m_test = 42; } return m_test; } int MyClass::lazy_InitializationViaPointer() { if( !m_test.isInitialized() ) { intFactoryViaPointer( & m_testViaInitializationOfPointer ); m_testViaInitializationOfPointer.forceInitialized(); } return m_testViaInitializationOfPointer; } Lazy<FILE*> MyClass::lazy_ResourceManagement() { Lazy<FILE*> lazyFile( /*open*/ []() { return fopen("test.txt", "w"); }, /*close*/ [](FILE*& h) { fclose(h); } ); return lazyFile; } private: static int intFactory() { return 42; } static void intFactoryViaPointer( int * v ) { *v = 42; } };

并且,这是代码.此版本使用stdc ++ 11库,但可以轻松转换为使用boost.

And, here is the code. This version uses the stdc++11 library, but can easily be converted to use boost.

Lazy.hpp

#pragma once #include <functional> #include <stdexcept> // Exception thrown on attempt to access an uninitialized Lazy struct uninitialized_lazy_exception : public std::runtime_error { uninitialized_lazy_exception() :std::runtime_error( "uninitialized lazy value" ) {} }; template<typename T> struct Lazy { // Default constructor Lazy() :m_bInitialized(false) ,m_initializer(DefaultInitializer) ,m_deinitializer(DefaultDeinitializer) { } // Construct with initializer and optional deinitializer functor Lazy( std::function<T(void)> initializer, std::function<void(T&)> deinitializer = DefaultDeinitializer ) :m_bInitialized(false) ,m_initializer(initializer) ,m_deinitializer(deinitializer) { } // Copy constructor Lazy( const Lazy& o ) :m_bInitialized(false) ,m_initializer(o.m_initializer) ,m_deinitializer(o.m_deinitializer) { if( o.m_bInitialized ) construct( *o.valuePtr() ); } // Assign from Lazy<T> Lazy& operator=( const Lazy<T>& o ) { destroy(); m_initializer = o.m_initializer; m_deinitializer = o.m_deinitializer; if( o.m_bInitialized ) construct(*o.valuePtr()); return *this; } // Construct from T Lazy( const T& v ) :m_bInitialized(false) ,m_initializer(DefaultInitializer) ,m_deinitializer(DefaultDeinitializer) { construct(v); } // Assign from T T& operator=(const T& value ) { construct(value); return *valuePtr(); } // Destruct and deinitialize ~Lazy() { destroy(); } // Answer true if initialized, either implicitly via function or explicitly via assignment bool isInitialized() const { return m_bInitialized; } // Force initialization, if not already done, and answer with the value // Throws exception if not implicitly or explicitly initialized T& force() const { if( !m_bInitialized ) { construct(m_initializer()); } return *valuePtr(); } // Implicitly force initialization and answer with value operator T&() const { return force(); } // Get pointer to storage of T, regardless of initialized state T* operator &() const { return valuePtr(); } // Force initialization state to true, e.g. if value initialized directly via pointer void forceInitialized() { m_bInitialized = true; } private: mutable char m_value[sizeof(T)]; mutable bool m_bInitialized; std::function<T(void)> m_initializer; std::function<void(T&)> m_deinitializer; // Get pointer to storage of T T* valuePtr() const { return static_cast<T*>( static_cast<void*>( &m_value ) ); } // Call copy constructor for T. Deinitialize self first, if necessary. void construct(const T& value) const { destroy(); new (valuePtr()) T(value); m_bInitialized = true; } // If initialized, call deinitializer and then destructor for T void destroy() const { if( m_bInitialized ) { m_deinitializer(*valuePtr()); valuePtr()->~T(); m_bInitialized = false; } } // Inititializer if none specified; throw exception on attempt to access uninitialized lazy static T DefaultInitializer() { throw uninitialized_lazy_exception(); } // Deinitialize if none specified; does nothing static void DefaultDeinitializer(T&) { } };

test_Lazy.cpp

#include "stdafx.h" #include "CppUnitTest.h" #include "Lazy.hpp" #include <memory> #include <string> using namespace std; using namespace Microsoft::VisualStudio::CppUnitTestFramework; namespace Lazy_Test { TEST_CLASS(test_Lazy) { public: TEST_METHOD(Lazy_ReturnsValueOnForce) { const Lazy<int> test( []() { return 42; } ); Assert::AreEqual( false, test.isInitialized() ); Assert::AreEqual( 42, test.force() ); Assert::AreEqual( true, test.isInitialized() ); } TEST_METHOD(Lazy_ManualInitialization) { Lazy<int> test; Assert::AreEqual( false, test.isInitialized() ); if( !test.isInitialized() ) { test = 42; } Assert::AreEqual( 42, (int)test ); Assert::AreEqual( 42, test.force() ); Assert::AreEqual( true, test.isInitialized() ); } TEST_METHOD(UninitializedLazy_ThrowsExceptionOnForce) { const Lazy<int> test; Assert::AreEqual( false, test.isInitialized() ); Assert::ExpectException<uninitialized_lazy_exception>( [&test]() { test.force(); } ); } TEST_METHOD(Lazy_ManualInitializationViaPointer) { Lazy<int> test; Assert::AreEqual( false, test.isInitialized() ); if( !test.isInitialized() ) { int* pTest = &test; *pTest = 42; test.forceInitialized(); } Assert::AreEqual( true, test.isInitialized() ); Assert::AreEqual( 42, (int)test ); Assert::AreEqual( 42, test.force() ); } TEST_METHOD(Lazy_DoesResourceDeinitialization) { typedef unsigned HANDLE; bool bIsOpen = false; // side-effect for unit testing function<HANDLE()> openLambda = [&bIsOpen]() { bIsOpen = true; return 12345; }; function<void(HANDLE&)> closeLambda = [&bIsOpen](HANDLE& h) { bIsOpen = false; // e.g.., Close(h); }; { Lazy<HANDLE> lazyHandle( openLambda, closeLambda ); Assert::AreEqual( false, bIsOpen ); HANDLE h = lazyHandle; Assert::AreEqual( true, bIsOpen ); Assert::AreEqual( (HANDLE)12345, h ); } Assert::AreEqual( false, bIsOpen ); } TEST_METHOD(Lazy_CopiesCorrectly) { const Lazy<int> a( []() { return 42; } ); Lazy<int> b; Lazy<int> c = (b = a); Assert::AreEqual( false, a.isInitialized() ); Assert::AreEqual( false, b.isInitialized() ); Assert::AreEqual( false, c.isInitialized() ); Assert::AreEqual( 42, a.force() ); Assert::AreEqual( true, a.isInitialized() ); Assert::AreEqual( false, b.isInitialized() ); Assert::AreEqual( false, c.isInitialized() ); Assert::AreEqual( 42, b.force() ); Assert::AreEqual( false, c.isInitialized() ); Assert::AreEqual( true, b.isInitialized() ); Assert::AreEqual( true, a.isInitialized() ); Assert::AreEqual( 42, c.force() ); Assert::AreEqual( true, c.isInitialized() ); Assert::AreEqual( true, b.isInitialized() ); Assert::AreEqual( true, a.isInitialized() ); } TEST_METHOD(Lazy_HandlesAssignment) { const Lazy<int> a ([]() { return 42; } ); Lazy<int> b; Assert::AreEqual( false, a.isInitialized() ); Assert::AreEqual( false, b.isInitialized() ); Assert::AreEqual( 42, a.force() ); Assert::AreEqual( true, a.isInitialized() ); Assert::AreEqual( false, b.isInitialized() ); b = 43; Assert::AreEqual( true, b.isInitialized() ); Assert::AreEqual( 43, b.force() ); b = a; Assert::AreEqual( true, b.isInitialized() ); Assert::AreEqual( 42, b.force() ); } TEST_METHOD(Lazy_HandlesNonTrivialClass) { Lazy<string> a( []() { return "fee"; } ); Lazy<string> b( string("fie") ); Lazy<string> c; c = "foe"; Lazy<string> d(c); Assert::AreEqual( string("fee"), (string)a ); Assert::AreEqual( string("fie"), (string)b ); Assert::AreEqual( string("foe"), (string)c ); Assert::AreEqual( string("foe"), (string)d ); d = "fum"; Assert::AreEqual( string("fum"), (string)d ); Assert::AreEqual( (size_t)3, a.force().length() ); Assert::AreEqual( (size_t)3, b.force().length() ); Assert::AreEqual( (size_t)3, c.force().length() ); Assert::AreEqual( (size_t)3, d.force().length() ); } struct NoDefaultConstructor { NoDefaultConstructor(bool v) : m_state(v) {} NoDefaultConstructor(const NoDefaultConstructor& o) : m_state(o.m_state) {} NoDefaultConstructor& operator=(const NoDefaultConstructor& o) { m_state=o.m_state; return *this; } bool operator !() { return !m_state; } private: bool m_state; }; TEST_METHOD(Lazy_HandlesNoDefaultConstructor) { Lazy<NoDefaultConstructor> v( []() { return NoDefaultConstructor(true); } ); Assert::IsTrue( !!v.force() ); } }; }

更多推荐

如何在C ++中抽象延迟初始化?

本文发布于:2023-11-12 01:56:47,感谢您对本站的认可!
本文链接:https://www.elefans.com/category/jswz/34/1580185.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文标签:初始化   抽象   如何在

发布评论

评论列表 (有 0 条评论)
草根站长

>www.elefans.com

编程频道|电子爱好者 - 技术资讯及电子产品介绍!