簡體   English   中英

使用非成員非友元函數代替成員函數:缺點?

[英]Using non-member non-friend functions instead of member functions: disadvantages?

Scott Meyers 長期以來一直提倡使用非成員非友元函數代替成員函數來改進封裝。 我可以看到這樣做的好處。

但是,在我看來,缺點如下:

我有一些自定義圖像類的自定義元數據類,其中包含許多數據成員。 有幾種格式可以保存圖像,並且必須將元數據轉換為這些格式可以采用的格式(ENVI、png、TIFF 等)。 現在我按照 Scott 的建議將這些轉換函數放入一個單獨的命名空間中。 他們本質上使用公共接口將所有成員復制到適合最終元數據格式的內容中,但他們需要包括所有數據成員。 例子:

// file Metadata.h
class Metadata
{
   // Getters
     std::string GetDescription() const;
     std::string GetTimeStamp() const;
     float       GetExposureTimeInMilliSeconds() const;
  // Setters
  // ...
   private:
     std::string m_description;
     std::string m_timeStamp;
     float       m_exposureTimeInMilliSeconds;

   // Added later with associated getters/setters:
   // std::string m_location;
   // std::string m_nameOfPersonWhoTookThePicture;
};

// File UtilityFunctions.h
namespace UtilityFunctions
{
    ENVIMetadata ConvertMetadataToENVIMetadata(const Metadata &i_metadata)\
    {
        ENVIMetadata envi;
        envi.AddMetadata<string>("Description", GetDescription());
        envi.AddMetadata<string>("Time stamp", GetTimeStamp());
        envi.AddMetadata<float>("Exposure time", GetExposureTimeInMilliSeconds());
    }
}

我看到的問題是,當其他人在該項目上工作並且該人將另一個數據成員添加到元數據時,他/她需要記住將此數據成員添加到所有轉換函數中。 由於它們位於不同的頭文件/cpp 文件中,因此很容易忘記這一點,而且我們有一個不明顯的錯誤,即並非所有數據成員都保存在元數據中。 如果函數是一個公共成員,查看頭文件(在添加新數據成員時)可能會提醒那個人也在那里添加成員,然后完成的必要性只會在那個文件中。

要點是,使用公共接口確實保證(如果接口沒有改變)如果類中的某些內容發生變化,基於它的函數將繼續工作,但是如果向類中添加了額外的特性,它並不能保證完整性,這也需要添加到這些功能中。

在某些情況下,人們會建議不要遵循這一建議嗎? 對於這種特定情況,是否有一些范式可以兩全其美?

我不確定我是否一定同意非成員與成員函數的討論,因為非成員肯定不會改進封裝。 無論如何,我建議在 C++17 中使用 結構化綁定來幫助解決這個問題。

// file Metadata.h
struct Metadata
{
     std::string m_description;
     std::string m_timeStamp;
     float       m_exposureTimeInMilliSeconds;

   // Added later:
   // std::string m_location;
   // std::string m_nameOfPersonWhoTookThePicture;
};

// File UtilityFunctions.h
namespace UtilityFunctions
{
    ENVIMetadata ConvertMetadataToENVIMetadata(const Metadata &metadata)
    {
        const auto& [description, timestamp, exposureTimeInMilliSeconds] =
            metadata;
        ENVIMetadata envi;
        envi.AddMetadata<string>("Description", description);
        envi.AddMetadata<string>("Time stamp", timeStamp);
        envi.AddMetadata<float>("Exposure time", exposureTimeInMilliSeconds);
    }
}

當稍后添加字段m_locationm_nameOfPersonWhoTookThePicture ,結構化綁定聲明將產生一個錯誤,指出您沒有提供足夠的標識符。

您可以提供元Metadata的類似元組的視圖,並讓轉換函數實例化std::index_sequence以填充結果;

// file Metadata.h
class Metadata
{
   // Getters
     std::string GetDescription() const;
     std::string GetTimeStamp() const;
     float       GetExposureTimeInMilliSeconds() const;

     template<size_t I> static const char * name();

  // Setters
  // ...
   private:
     std::string m_description;
     std::string m_timeStamp;
     float       m_exposureTimeInMilliSeconds;

   // Added later with associated getters/setters:
   // std::string m_location;
   // std::string m_nameOfPersonWhoTookThePicture;
};

namespace std
{
    template<> class tuple_size<Metadata> : public std::integral_constant<std::size_t, 3> {}; // later 5

    template<> class tuple_element<0, Metadata>{ using type = std::string; };
    template<> class tuple_element<1, Metadata>{ using type = std::string; };
    template<> class tuple_element<2, Metadata>{ using type = float; };
    /* Later add
    template<> class tuple_element<3, Metadata>{ using type = std::string; };
    template<> class tuple_element<4, Metadata>{ using type = std::string; };
    */
}

template<size_t I> std::tuple_element_t<I, Metadata> get(const Metadata & meta);
template<> std::string get<0>(const Metadata & meta) { return meta.GetDescription(); }
template<> std::string get<1>(const Metadata & meta) { return meta.GetTimeStamp(); }
template<> float get<2>(const Metadata & meta) { return meta.GetExposureTimeInMilliSeconds(); }
/* Later add
template<> std::string get<3>(const Metadata & meta) { return meta.GetLocation(); }
template<> std::string get<4>(const Metadata & meta) { return meta.GetPhotographerName(); }
*/

template<> const char * Metadata::name<0>() { return "Description"; }
template<> const char * Metadata::name<1>() { return "Time Stamp"; }
template<> const char * Metadata::name<2>() { return "Exposure Time"; }
/* Later add
template<> const char * Metadata::name<3>() { return "Location"; }
template<> const char * Metadata::name<2>() { return "PhotographerName"; }
*/

添加成員時,轉換函數不會改變

// File UtilityFunctions.h
namespace UtilityFunctions
{
    namespace detail
    {
        template<size_t... Is>
        ENVIMetadata ConvertMetadataToENVIMetadata(const Metadata &i_metadata, std::index_sequence<Is...>)
        {
            ENVIMetadata envi;
            envi.AddMetadata<std::tuple_element_t<Is, Metadata>>(Metadata::name<Is>(), get<Is>(i_metadata))...;
            return envi;
        }
    }

    ENVIMetadata ConvertMetadataToENVIMetadata(const Metadata &i_metadata)\
    {
        return detail::ConvertMetadataToENVIMetadata(i_metadata, std::make_index_sequence<std::tuple_size_v<Metadata>>{})
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM