সুরক্ষিত উত্তরাধিকারের বিপরীতে, সি ++ ব্যক্তিগত উত্তরাধিকার মূলধারার সি ++ বিকাশে প্রবেশের পথ খুঁজে পেয়েছে। তবে, এখনও আমি এটির জন্য ভাল ব্যবহার খুঁজে পাইনি।
আপনি ছেলেরা কখন এটি ব্যবহার করবেন?
সুরক্ষিত উত্তরাধিকারের বিপরীতে, সি ++ ব্যক্তিগত উত্তরাধিকার মূলধারার সি ++ বিকাশে প্রবেশের পথ খুঁজে পেয়েছে। তবে, এখনও আমি এটির জন্য ভাল ব্যবহার খুঁজে পাইনি।
আপনি ছেলেরা কখন এটি ব্যবহার করবেন?
উত্তর:
উত্তর গ্রহণের পরে নোট করুন: এটি একটি সম্পূর্ণ উত্তর নয়। আপনি যদি প্রশ্নটিতে আগ্রহী হন তবে এখানে (ধারণাগতভাবে) এবং এখানে (তাত্ত্বিক এবং ব্যবহারিক উভয় ) মতো অন্যান্য উত্তরগুলি পড়ুন । এটি কেবল একটি অভিনব কৌশল যা ব্যক্তিগত উত্তরাধিকারের সাথে অর্জন করা যায়। এটি অভিনব হলেও এটি প্রশ্নের উত্তর নয়।
সি ++ এফএকিউতে প্রদর্শিত কেবলমাত্র ব্যক্তিগত উত্তরাধিকারের বুনিয়াদি ব্যবহার ছাড়াও (অন্যের মন্তব্যে লিঙ্কিত) আপনি কোনও শ্রেণি (। নেট টার্মিনোলজিতে) সিল করতে বা শ্রেণি ফাইনাল করতে (জাভা পরিভাষায়) একটি ব্যক্তিগত বা ভার্চুয়াল উত্তরাধিকারের সংমিশ্রণ ব্যবহার করতে পারেন । এটি সাধারণ ব্যবহার নয়, তবে যাইহোক আমি এটি আকর্ষণীয় বলে মনে করেছি:
class ClassSealer {
private:
friend class Sealed;
ClassSealer() {}
};
class Sealed : private virtual ClassSealer
{
// ...
};
class FailsToDerive : public Sealed
{
// Cannot be instantiated
};
সিল তাত্ক্ষণিক করা যেতে পারে। এটি ক্লাসসিলার থেকে প্রাপ্ত এবং এটি বন্ধু হিসাবে সরাসরি প্রাইভেট কনস্ট্রাক্টরকে কল করতে পারে।
FailsToDerive কম্পাইল না যেমন কল করতে হবে ClassSealer কন্সট্রাকটর সরাসরি (ভার্চুয়াল উত্তরাধিকার প্রয়োজন), কিন্তু এটা ব্যক্তিগত নয় হিসাবে পারেন নামমুদ্রাম্কিত শ্রেণী এবং এই ক্ষেত্রে FailsToDerive এক বন্ধু নয় ClassSealer ।
সম্পাদনা
মন্তব্যে উল্লেখ করা হয়েছিল যে সিআরটিপি ব্যবহারের সময় এটিকে জেনেরিক করা যায় না। সি ++ ১১ টি স্ট্যান্ডার্ড আর্গুমেন্টের সাথে বন্ধুত্বের জন্য আলাদা সিনট্যাক্স সরবরাহ করে সেই সীমাবদ্ধতাটিকে সরিয়ে দেয়:
template <typename T>
class Seal {
friend T; // not: friend class T!!!
Seal() {}
};
class Sealed : private virtual Seal<Sealed> // ...
অবশ্যই এটি সমস্ত বিচক্ষণ, যেহেতু সি ++ 11 final
ঠিক এই উদ্দেশ্যে একটি প্রাসঙ্গিক কীওয়ার্ড সরবরাহ করে:
class Sealed final // ...
আমি সব সময় এটি ব্যবহার করি। আমার মাথার উপরের অংশের কয়েকটি উদাহরণ:
একটি সাধারণ উদাহরণ একটি এসটিএল ধারক থেকে ব্যক্তিগতভাবে প্রাপ্ত হয়:
class MyVector : private vector<int>
{
public:
// Using declarations expose the few functions my clients need
// without a load of forwarding functions.
using vector<int>::push_back;
// etc...
};
push_back
, MyVector
বিনামূল্যে পান।
template<typename... Args> constexpr decltype(auto) f(Args && ... args) noexcept(noexcept(std::declval<Base &>().f(std::forward<Args>(args)...)) and std::is_nothrow_move_constructible<decltype(std::declval<Base &>().f(std::forward<Args>(args)...))>) { return m_base.f(std::forward<Args>(args)...); }
বা ব্যবহার করে লিখতে পারেন Base::f;
। আপনি যদি ব্যক্তিগত উত্তরাধিকার এবং একটি using
বিবৃতি আপনাকে প্রদত্ত বেশিরভাগ কার্যকারিতা এবং নমনীয়তা চান তবে প্রতিটি ফাংশনের জন্য আপনার কাছে সেই দৈত্য রয়েছে (এবং ভুলে যাবেন না const
এবং volatile
ওভারলোডগুলি!)।
ব্যক্তিগত উত্তরাধিকারের প্রথাগত ব্যবহার হ'ল সম্পর্কের ক্ষেত্রে "প্রয়োগ করা" (এই শব্দটির জন্য স্কট মেয়ার্সের 'কার্যকর সি ++' কে ধন্যবাদ)। অন্য কথায়, উত্তরাধিকার সূত্রে প্রাপ্ত শ্রেণীর বাহ্যিক ইন্টারফেসের উত্তরাধিকারসূত্রে প্রাপ্ত শ্রেণীর সাথে কোনও (দৃশ্যমান) সম্পর্ক নেই, তবে এটি এর কার্যকারিতা বাস্তবায়নের জন্য এটি অভ্যন্তরীণভাবে ব্যবহার করে।
ব্যক্তিগত উত্তরাধিকারের একটি দরকারী ব্যবহার হ'ল আপনি যখন একটি ক্লাস রাখেন যা একটি ইন্টারফেস প্রয়োগ করে, তারপরে এটি অন্য কোনও বস্তুর সাথে নিবন্ধিত হয়। আপনি সেই ইন্টারফেসটিকে ব্যক্তিগত করে তুলুন যাতে ক্লাসে নিজেই নিবন্ধন করতে হয় এবং এটির সাথে নিবন্ধীকৃত নির্দিষ্ট অবজেক্টগুলি সেই ফাংশনগুলি ব্যবহার করতে পারে।
উদাহরণ স্বরূপ:
class FooInterface
{
public:
virtual void DoSomething() = 0;
};
class FooUser
{
public:
bool RegisterFooInterface(FooInterface* aInterface);
};
class FooImplementer : private FooInterface
{
public:
explicit FooImplementer(FooUser& aUser)
{
aUser.RegisterFooInterface(this);
}
private:
virtual void DoSomething() { ... }
};
সুতরাং FooUser ক্লাস FooImplementer এর ব্যক্তিগত পদ্ধতিগুলি FooInterface ইন্টারফেসের মাধ্যমে কল করতে পারে, অন্য অন্যান্য বাহ্যিক শ্রেণিগুলি এটি পারে না। ইন্টারফেস হিসাবে সংজ্ঞায়িত নির্দিষ্ট কলব্যাকগুলি পরিচালনা করার জন্য এটি দুর্দান্ত প্যাটার্ন।
আমি মনে করি সি ++ এফএকিউ লাইটের সমালোচনা বিভাগটি হ'ল:
ব্যক্তিগত উত্তরাধিকারের জন্য একটি বৈধ, দীর্ঘমেয়াদী ব্যবহার হ'ল আপনি যখন একটি ক্লাস ফ্রেড তৈরি করতে চান যা ক্লাস উইলমাতে কোড ব্যবহার করে এবং ক্লাস উইলমা থেকে কোডটি আপনার নতুন ক্লাস ফ্রেডের সদস্য ফাংশন আহ্বান করতে হবে। এক্ষেত্রে ফ্রেড উইলমাতে অ-ভার্চুয়ালকে কল করেন এবং উইলমা কল করে (সাধারণত খাঁটি ভার্চুয়াল) নিজের মধ্যে, যা ফ্রেডের দ্বারা ওভাররাইড হয়ে যায়। এটি কম্পোজিশনের সাথে করা আরও শক্ত হবে।
সন্দেহ হলে, আপনার ব্যক্তিগত উত্তরাধিকারের চেয়ে রচনাটি পছন্দ করা উচিত।
আমি ইন্টারফেসের (যেমন অ্যাবস্ট্রাক্ট ক্লাস) জন্য দরকারী বলে মনে করি যে আমি উত্তরাধিকার সূত্রে পাই যেখানে আমি অন্য কোডটি ইন্টারফেসটি স্পর্শ করতে চাই না (কেবল উত্তরাধিকারী শ্রেণি)।
[একটি উদাহরণে সম্পাদিত]
উপরে লিঙ্কিত উদাহরণ নিন । যে বলার অপেক্ষা রাখে না
[...] ক্লাস উইলমা আপনার নতুন ক্লাস ফ্রেডের সদস্য ফাংশন আহ্বান করতে হবে।
বলার অপেক্ষা রাখে না যে উইলমা ফ্রেডকে নির্দিষ্ট সদস্য ফাংশন আহ্বান করতে সক্ষম হতে বা তার পরিবর্তে এটি বলছে যে উইলমা একটি ইন্টারফেস । সুতরাং, উদাহরণ হিসাবে উল্লিখিত
ব্যক্তিগত উত্তরাধিকার মন্দ নয়; এটি বজায় রাখা আরও ব্যয়বহুল, যেহেতু এটির সম্ভাবনা বাড়িয়ে দেয় যে কেউ আপনার কোড ভঙ্গ করে এমন কিছু পরিবর্তন করবে।
প্রোগ্রামারদের আমাদের ইন্টারফেসের প্রয়োজনীয়তাগুলি পূরণ করতে, বা কোড ভঙ্গ করতে প্রয়োজনীয় কাঙ্ক্ষিত প্রভাব সম্পর্কে মন্তব্য। এবং, যেহেতু ফ্রেডকলস উইলমা () কেবলমাত্র বন্ধু এবং উদ্ভূত শ্রেণিগুলি এটি স্পর্শ করতে পারে অর্থাৎ উত্তরাধিকারসূত্রে প্রাপ্ত ইন্টারফেস (বিমূর্ত শ্রেণি) যা কেবল উত্তরাধিকার সূত্রে প্রাপ্ত শ্রেণি (এবং বন্ধুরা) স্পর্শ করতে পারে।
[অন্য একটি উদাহরণে সম্পাদিত]
এই পৃষ্ঠাটি সংক্ষেপে বেসরকারী ইন্টারফেসগুলি (অন্য কোনও কোণ থেকে) নিয়ে আলোচনা করেছে।
কখনও কখনও আমি অন্যের ইন্টারফেসে একটি ছোট ইন্টারফেস (উদাহরণস্বরূপ একটি সংগ্রহ) প্রকাশ করতে চাইলে ব্যক্তিগত উত্তরাধিকার ব্যবহার করা আমার পক্ষে দরকারী মনে হয়, যেখানে সংগ্রহের বাস্তবায়নটি অভ্যন্তরীণ শ্রেণীর মতো একইভাবে, বহির্মুখী শ্রেণীর রাজ্যে প্রবেশাধিকার প্রয়োজন where জাভা।
class BigClass;
struct SomeCollection
{
iterator begin();
iterator end();
};
class BigClass : private SomeCollection
{
friend struct SomeCollection;
SomeCollection &GetThings() { return *this; }
};
তারপরে যদি সাম্য সংগ্রহের বিগক্লাস অ্যাক্সেসের প্রয়োজন হয় তবে তা পারে static_cast<BigClass *>(this)
। কোনও অতিরিক্ত ডেটা সদস্য স্থান নেওয়ার দরকার নেই।
BigClass
এই উদাহরণে কি আছে? আমি এটি আকর্ষণীয় মনে করি, কিন্তু এটি আমার মুখে হ্যাকিশের চিৎকার করে।
আমি ব্যক্তিগত উত্তরাধিকারের জন্য একটি দুর্দান্ত অ্যাপ্লিকেশন পেয়েছি, যদিও এর সীমিত ব্যবহার রয়েছে।
ধরুন আপনাকে নিম্নলিখিত সিপিআই দেওয়া হয়েছে:
#ifdef __cplusplus
extern "C" {
#endif
typedef struct
{
/* raw owning pointer, it's C after all */
char const * name;
/* more variables that need resources
* ...
*/
} Widget;
Widget const * loadWidget();
void freeWidget(Widget const * widget);
#ifdef __cplusplus
} // end of extern "C"
#endif
এখন আপনার কাজ হ'ল সি ++ ব্যবহার করে এই এপিআই প্রয়োগ করা।
অবশ্যই আমরা এর মতো সি-ইশ বাস্তবায়ন স্টাইল বেছে নিতে পারি:
Widget const * loadWidget()
{
auto result = std::make_unique<Widget>();
result->name = strdup("The Widget name");
// More similar assignments here
return result.release();
}
void freeWidget(Widget const * const widget)
{
free(result->name);
// More similar manual freeing of resources
delete widget;
}
তবে কয়েকটি অসুবিধা রয়েছে:
struct
ভুল সেট আপ করা সহজstruct
আমাদের সি ++ ব্যবহার করার অনুমতি দেওয়া হচ্ছে, তবে এর সম্পূর্ণ শক্তি কেন ব্যবহার করবেন না?
উপরের সমস্যাগুলি মূলত সমস্ত ম্যানুয়াল রিসোর্স ম্যানেজমেন্টের সাথে আবদ্ধ। যে সমাধানটি মনে আসে তা হ'ল উত্তরাধিকার সূত্রে Widget
প্রাপ্ত WidgetImpl
এবং প্রতিটি ভেরিয়েবলের জন্য উত্পন্ন শ্রেণিতে একটি উত্স পরিচালনার উদাহরণ যুক্ত করা :
class WidgetImpl : public Widget
{
public:
// Added bonus, Widget's members get default initialized
WidgetImpl()
: Widget()
{}
void setName(std::string newName)
{
m_nameResource = std::move(newName);
name = m_nameResource.c_str();
}
// More similar setters to follow
private:
std::string m_nameResource;
};
এটি নিম্নলিখিতগুলিতে বাস্তবায়নকে সহজতর করে:
Widget const * loadWidget()
{
auto result = std::make_unique<WidgetImpl>();
result->setName("The Widget name");
// More similar setters here
return result.release();
}
void freeWidget(Widget const * const widget)
{
// No virtual destructor in the base class, thus static_cast must be used
delete static_cast<WidgetImpl const *>(widget);
}
এটির মতো আমরা উপরের সমস্ত সমস্যার প্রতিকার করেছি। তবে একটি ক্লায়েন্ট এখনও সেটারগুলি সম্পর্কে ভুলে যেতে WidgetImpl
পারে এবং Widget
সরাসরি সদস্যদের অর্পণ করতে পারে ।
Widget
সদস্যদের encapsulate করতে আমরা ব্যক্তিগত উত্তরাধিকার ব্যবহার করি। দু: খজনকভাবে এখন আমাদের উভয় শ্রেণির মধ্যে কাস্ট করতে দুটি অতিরিক্ত ফাংশন প্রয়োজন:
class WidgetImpl : private Widget
{
public:
WidgetImpl()
: Widget()
{}
void setName(std::string newName)
{
m_nameResource = std::move(newName);
name = m_nameResource.c_str();
}
// More similar setters to follow
Widget const * toWidget() const
{
return static_cast<Widget const *>(this);
}
static void deleteWidget(Widget const * const widget)
{
delete static_cast<WidgetImpl const *>(widget);
}
private:
std::string m_nameResource;
};
এটি নিম্নলিখিত রূপান্তরগুলি প্রয়োজনীয় করে তোলে:
Widget const * loadWidget()
{
auto widgetImpl = std::make_unique<WidgetImpl>();
widgetImpl->setName("The Widget name");
// More similar setters here
auto const result = widgetImpl->toWidget();
widgetImpl.release();
return result;
}
void freeWidget(Widget const * const widget)
{
WidgetImpl::deleteWidget(widget);
}
এই সমাধানটি সমস্ত সমস্যার সমাধান করে। কোনও ম্যানুয়াল মেমরি ম্যানেজমেন্ট নেই এবং Widget
সুন্দরভাবে এনপ্যাপসুলেটেড রয়েছে যাতে WidgetImpl
কোনও পাবলিক ডেটা সদস্য না থাকে। এটি কার্যকরভাবে সঠিকভাবে ব্যবহার করা সহজ এবং ভুল ব্যবহার করা সহজ (অসম্ভব?)।
কোড স্নিপেটগুলি কলিরুতে একটি সংকলন উদাহরণ তৈরি করে ।
যদি উত্পন্ন শ্রেণি - কোডটি পুনরায় ব্যবহার করতে হবে এবং - আপনি বেস শ্রেণিটি পরিবর্তন করতে পারবেন না এবং - লকের নিচে বেসের সদস্যদের ব্যবহার করে এর পদ্ধতিগুলি রক্ষা করছেন।
তারপরে আপনার ব্যক্তিগত উত্তরাধিকার ব্যবহার করা উচিত, অন্যথায় আপনার উত্পন্ন শ্রেণীর মাধ্যমে রফতানি করা আনলক করা বেস পদ্ধতিগুলির ঝুঁকি রয়েছে।
সম্পর্ক "একটি" না হলে ব্যক্তিগত উত্তরাধিকার ব্যবহার করা হবে, তবে নতুন শ্রেণিটি "বিদ্যমান শ্রেণীর মেয়াদে" প্রয়োগ করা যেতে পারে বা নতুন শ্রেণি "বিদ্যমান শ্রেণীর মতো" কাজ করা যেতে পারে।
"আন্দ্রেই আলেকজান্দ্রেস্কু, হার্ব সুটার দ্বারা কোডিং স্ট্যান্ডার্ড" থেকে উদাহরণ: - বিবেচনা করুন যে দুটি শ্রেণি স্কয়ার এবং আয়তক্ষেত্রের প্রত্যেকটির উচ্চতা এবং প্রস্থ নির্ধারণের জন্য ভার্চুয়াল ফাংশন রয়েছে। তারপরে স্কোয়ারটি সঠিকভাবে আয়তক্ষেত্রের কাছ থেকে উত্তরাধিকারী হতে পারে না, কারণ একটি পরিবর্তনযোগ্য আয়তক্ষেত্র ব্যবহার করে এমন কোডটি ধরে নেবে যে সেটউইথটি উচ্চতা পরিবর্তন করে না (আয়তক্ষেত্র স্পষ্টভাবে সেই নথিটি চুক্তি করে বা না), অন্যদিকে স্কোয়ার :: সেটউইথ যে চুক্তিটি এবং তার নিজস্ব স্কোয়ারনেস ইনগ্রানিয়েন্ট সংরক্ষণ করতে পারে না একই সময়. তবে স্কোয়ারের ক্লায়েন্টরা উদাহরণস্বরূপ স্কয়ারের কাছ থেকে সঠিকভাবে উত্তরাধিকারী হতে পারে না, যদি স্কয়ারের ক্লায়েন্টরা উদাহরণস্বরূপ বিবেচনা করে যে কোনও স্কোয়ারের ক্ষেত্রফলটি এর প্রস্থ বর্গক্ষেত্র, বা যদি তারা অন্য কোনও সম্পত্তিতে নির্ভর করে যা আয়তক্ষেত্রের জন্য নেই।
একটি বর্গক্ষেত্র "is-a" আয়তক্ষেত্র (গাণিতিক) তবে একটি স্কোয়ার একটি আয়তক্ষেত্র নয় (আচরণগতভাবে)। ফলস্বরূপ, বর্ণনাকে ভুল বোঝাবুঝির ঝুঁকিতে পরিণত করার জন্য আমরা "is-a" এর পরিবর্তে "" work-like-a "(বা, যদি আপনি পছন্দ করেন" "ব্যবহারযোগ্য-তে-এ") বলতে পছন্দ করি।
একটি শ্রেণি একটি আক্রমণকারী রাখে। আক্রমণকারীটি কনস্ট্রাক্টর দ্বারা প্রতিষ্ঠিত হয়। তবে, অনেক পরিস্থিতিতে অবজেক্টের প্রতিনিধিত্বের অবস্থার (যা আপনি নেটওয়ার্কের মাধ্যমে সঞ্চার করতে পারেন বা কোনও ফাইলে সংরক্ষণ করতে পারেন - ডিটিও যদি আপনি চান তবে) এর ধারণা রাখা দরকারী। সমষ্টিগত টাইপের ক্ষেত্রে REST সুনির্দিষ্টভাবে করা হয়। আপনি যদি সঠিক হন তবে এটি বিশেষভাবে সত্য। বিবেচনা:
struct QuadraticEquationState {
const double a;
const double b;
const double c;
// named ctors so aggregate construction is available,
// which is the default usage pattern
// add your favourite ctors - throwing, try, cps
static QuadraticEquationState read(std::istream& is);
static std::optional<QuadraticEquationState> try_read(std::istream& is);
template<typename Then, typename Else>
static std::common_type<
decltype(std::declval<Then>()(std::declval<QuadraticEquationState>()),
decltype(std::declval<Else>()())>::type // this is just then(qes) or els(qes)
if_read(std::istream& is, Then then, Else els);
};
// this works with QuadraticEquation as well by default
std::ostream& operator<<(std::ostream& os, const QuadraticEquationState& qes);
// no operator>> as we're const correct.
// we _might_ (not necessarily want) operator>> for optional<qes>
std::istream& operator>>(std::istream& is, std::optional<QuadraticEquationState>);
struct QuadraticEquationCache {
mutable std::optional<double> determinant_cache;
mutable std::optional<double> x1_cache;
mutable std::optional<double> x2_cache;
mutable std::optional<double> sum_of_x12_cache;
};
class QuadraticEquation : public QuadraticEquationState, // private if base is non-const
private QuadraticEquationCache {
public:
QuadraticEquation(QuadraticEquationState); // in general, might throw
QuadraticEquation(const double a, const double b, const double c);
QuadraticEquation(const std::string& str);
QuadraticEquation(const ExpressionTree& str); // might throw
}
এই মুহুর্তে, আপনি কেবল পাত্রে ক্যাশের সংগ্রহগুলি সঞ্চয় করতে পারেন এবং এটি নির্মাণের জন্য সন্ধান করতে পারেন। কিছু বাস্তব প্রক্রিয়াজাতকরণ আছে যদি হ্যান্ড। নোট করুন ক্যাশে QE এর অংশ: QE- এ সংজ্ঞায়িত ক্রিয়াকলাপের অর্থ ক্যাশে আংশিক পুনরায় ব্যবহারযোগ্য (যেমন, সি যোগফলকে প্রভাবিত করে না); তবুও, যখন কোনও ক্যাশে নেই, এটি সন্ধান করা মূল্যবান।
ব্যক্তিগত উত্তরাধিকার প্রায় সর্বদা সদস্য দ্বারা মডেল করা যেতে পারে (প্রয়োজনে বেসের রেফারেন্স সঞ্চয় করে)। এইভাবে মডেল করার পক্ষে এটি সর্বদা মূল্যবান নয়; কখনও কখনও উত্তরাধিকার সবচেয়ে দক্ষ প্রতিনিধিত্ব হয়।
আপনার যদি std::ostream
কিছু ছোট পরিবর্তন (যেমন এই প্রশ্নের মতো ) থাকে তবে আপনার প্রয়োজন হতে পারে
MyStreambuf
যা উত্স থেকে এসেছে std::streambuf
এবং সেখানে পরিবর্তনগুলি প্রয়োগ করেMyOStream
যা থেকে উদ্ভূত std::ostream
হয় এবং এটির উদাহরণটি শুরু করে এবং পরিচালনা করে MyStreambuf
এবং পয়েন্টারটিকে সেই কনস্ট্রাক্টরের কাছে দেয়std::ostream
প্রথম ধারণাটি ক্লাসে MyStream
ডেটা সদস্য হিসাবে উদাহরণ যুক্ত করা হতে পারে MyOStream
:
class MyOStream : public std::ostream
{
public:
MyOStream()
: std::basic_ostream{ &m_buf }
, m_buf{}
{}
private:
MyStreambuf m_buf;
};
তবে বেস ক্লাসগুলি কোনও ডেটা মেম্বার এর আগে তৈরি করা হয় যাতে আপনি একটি এখনও নির্মিত না হওয়া std::streambuf
উদাহরণটিতে পয়েন্টার দিয়ে std::ostream
যাচ্ছেন যা অবধারিত আচরণ।
উপরোক্ত প্রশ্নের উত্তর বেনের উত্তরটিতে সমাধানটির প্রস্তাব দেওয়া হয়েছে , প্রথমে স্ট্রিম বাফার থেকে প্রথমে উত্তোলন করুন, তারপরে প্রবাহ থেকে এবং তারপরে স্ট্রিমটি আরম্ভ করুন this
:
class MyOStream : public MyStreamBuf, public std::ostream
{
public:
MyOStream()
: MyStreamBuf{}
, basic_ostream{ this }
{}
};
তবে ফলস্বরূপ শ্রেণিটি std::streambuf
উদাহরণ হিসাবে ব্যবহৃত হতে পারে যা সাধারণত অনাকাঙ্ক্ষিত। ব্যক্তিগত উত্তরাধিকারে স্যুইচ করা এই সমস্যার সমাধান করে:
class MyOStream : private MyStreamBuf, public std::ostream
{
public:
MyOStream()
: MyStreamBuf{}
, basic_ostream{ this }
{}
};
কেবল সি ++ এর বৈশিষ্ট্য থাকার কারণে, এটি দরকারী নয় বা এটি ব্যবহার করা উচিত।
আমি বলব যে আপনি এটি ব্যবহার করা উচিত নয়।
আপনি যদি যাইহোক এটি ব্যবহার করে থাকেন তবে ভাল, আপনি মূলত এনক্যাপসুলেশন লঙ্ঘন করছেন এবং সংহতি হ্রাস করছেন। আপনি একটি শ্রেণিতে ডেটা রাখছেন, এবং এমন পদ্ধতিগুলি যুক্ত করছেন যা অন্য ক্লাসে ডেটা ম্যানিপুলেট করে।
অন্যান্য সি ++ বৈশিষ্ট্যগুলির মতো এটিও ক্লাস সিল করার মতো পার্শ্ব প্রতিক্রিয়া অর্জন করতে ব্যবহার করা যেতে পারে (ড্রিবিয়াসের উত্তরে উল্লিখিত), তবে এটি এটি একটি ভাল বৈশিষ্ট্য করে না।