কেন আমরা স্টাড :: ভেক্টর <অ্যাবস্ট্রাক্ট ক্লাস> ঘোষণা করতে পারি না?


89

সি # তে বিকাশের জন্য বেশ কিছুটা সময় ব্যয় করে আমি লক্ষ্য করেছি যে আপনি যদি এটি কোনও ইন্টারফেস হিসাবে ব্যবহারের উদ্দেশ্যে কোনও বিমূর্ত শ্রেণির ঘোষণা করেন তবে আপনি এই বিমূর্ত শ্রেণির কোনও ভেক্টরকে বাচ্চাদের ক্লাসের উদাহরণ সংরক্ষণ করতে পারবেন না।

#pragma once
#include <iostream>
#include <vector>

using namespace std;

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

class FunnyContainer
{
private:
    std::vector <IFunnyInterface> funnyItems;
};

বিমূর্ত শ্রেণীর ভেক্টর ঘোষণাকারী লাইন এমএস ভিএস ২০০৫ এ এই ত্রুটির কারণ ঘটায়:

error C2259: 'IFunnyInterface' : cannot instantiate abstract class

আমি একটি সুস্পষ্ট কর্মক্ষেত্র দেখতে পাচ্ছি, যা নীচে IFunnyInterface প্রতিস্থাপন করবে:

class IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        throw new std::exception("not implemented");
    }
};

এটি কি গ্রহণযোগ্য কাজের সি ++ বুদ্ধিমান? যদি তা না হয় তবে বুস্টের মতো কোনও তৃতীয় পক্ষের লাইব্রেরি আছে যা আমাকে এটিকে পেতে সহায়তা করতে পারে?

ইহা পড়ার জন্য আপনাকে ধন্যবাদ !

অ্যান্টনি

উত্তর:


128

আপনি বিমূর্ত ক্লাস ইনস্ট্যান্ট করতে পারবেন না, সুতরাং বিমূর্ত শ্রেণির ভেক্টর কাজ করতে পারে না।

তবে আপনি বিমূর্ত ক্লাসগুলিতে পয়েন্টারগুলির একটি ভেক্টর ব্যবহার করতে পারেন:

std::vector<IFunnyInterface*> ifVec;

এটি আপনাকে প্রকৃতপক্ষে পলিমারফিক আচরণ ব্যবহার করতে সহায়তা করে - এমনকি শ্রেণিটি বিমূর্ত না থাকলেও মান অনুসারে সংরক্ষণ করলে অবজেক্ট স্লাইসিংয়ের সমস্যা দেখা দেয় ।


4
অথবা আপনি std :: vector <std :: tr1 :: shared_ptr <IFunnyInterface>> ব্যবহার করতে পারেন যদি আপনি নিজের হাতে বস্তুটির জীবনকাল মোকাবেলা করতে না চান।
সের্গেই টেপলিয়াভভ

4
বা আরও ভাল, উত্সাহিত :: ptr_vector <>।
রোল

7
বা এখন, স্ট্যান্ড :: ভেক্টর <স্টাডি :: অনন্য_আপনার <আইফুনি ইন্টারফেস>>।
কাজ ড্রাগন

21

আপনি একটি বিমূর্ত শ্রেণীর ধরণের ভেক্টর তৈরি করতে পারবেন না কারণ আপনি কোনও বিমূর্ত শ্রেণীর উদাহরণ তৈরি করতে পারবেন না, এবং সি ++ স্ট্যান্ডার্ড লাইব্রেরি পাত্রে স্ট্যান্ড :: ভেক্টর স্টোর মানগুলি (যেমন উদাহরণস্বরূপ) তৈরি করতে পারবেন না। আপনি যদি এটি করতে চান তবে আপনাকে বিমূর্ত শ্রেণীর প্রকারের জন্য পয়েন্টারগুলির একটি ভেক্টর তৈরি করতে হবে।

আপনার কর্মক্ষেত্রটি কাজ করবে না কারণ ভার্চুয়াল ফাংশন (যে কারণে আপনি প্রথম স্থানে বিমূর্ত শ্রেণি চান) কেবলমাত্র পয়েন্টার বা রেফারেন্সের মাধ্যমে ডাকলে কাজ করে। আপনি উভয়ই রেফারেন্সের ভেক্টর তৈরি করতে পারবেন না, সুতরাং এটি দ্বিতীয় কারণ যা আপনাকে অবশ্যই পয়েন্টারগুলির একটি ভেক্টর ব্যবহার করবে।

আপনার বুঝতে হবে যে সি ++ এবং সি # এর মধ্যে খুব কম মিল রয়েছে। আপনি যদি সি ++ শিখতে চান তবে আপনার এটিকে স্ক্র্যাচ থেকে শুরু করার মতো মনে করা উচিত, এবং কোনও উত্সাহিত সি ++ টিউটোরিয়াল যেমন কোয়েনিগ এবং মু দ্বারা এক্সিলারেটেড সি ++ পড়া উচিত


পোস্টে জবাব দেওয়ার পাশাপাশি একটি বই সুপারিশ করার জন্য আপনাকে ধন্যবাদ!
ব্লুট্রিন

তবে আপনি যখন বিমূর্ত ক্লাসগুলির একটি ভেক্টর ঘোষণা করেন, আপনি কোনও অ্যাবস্ট্রাক্ট ক্লাস তৈরি করতে বলছেন না, কেবল একটি ভেক্টর যা এই শ্রেণীর একটি নন অ্যাবস্ট্রাক্ট সাবক্লাস ধারণ করতে সক্ষম? আপনি যদি ভেক্টর কনস্ট্রাক্টরের কোনও নম্বর না দিয়ে থাকেন তবে এটি কীভাবে সম্ভব জানতে পারে যে বিমূর্ত শ্রেণীর কতগুলি উদাহরণ তৈরি হবে?
জোনাথন

6

এই ক্ষেত্রে আমরা এই কোডটিও ব্যবহার করতে পারি না:

std::vector <IFunnyInterface*> funnyItems;

বা

std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;

কারণ ফানিআইম্পল এবং আইফুনিআইন্টারফেসের মধ্যে কোনও আইএসের সম্পর্ক নেই এবং ব্যক্তিগত উত্তরাধিকারের কারণে FUnnyImpl এবং IFunnyInterface এর মধ্যে কোনও অন্তর্নিহিত রূপান্তর নেই।

আপনার কোডটি নিম্নলিখিত হিসাবে আপডেট করা উচিত:

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: public IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

4
বেশিরভাগ লোকেরা ব্যক্তিগত উত্তরাধিকারের দিকে তাকিয়েছিল বলে আমি মনে করি :) তবে আসুন আমরা
ওপিটিকে

4
হ্যাঁ বিশেষত টপিক স্টার্টারের এই বাক্যটির পরে: "সি # তে বিকাশের জন্য বেশ কিছুটা সময় ব্যয় করা" (যেখানে কোনও ব্যক্তিগত উত্তরাধিকার একেবারেই নেই)।
সের্গেই টেপলিয়াভভ

6

vectorTraditionalতিহ্যবাহী বিকল্পটি ইতিমধ্যে উল্লিখিত মত পয়েন্টার ব্যবহার করা ।

যারা প্রশংসা করেন তাদের জন্য Boostএকটি খুব আকর্ষণীয় লাইব্রেরি উপস্থিত হয়েছে: Pointer Containersযা কার্যের জন্য পুরোপুরি উপযোগী এবং আপনাকে পয়েন্টার দ্বারা বর্ণিত বিভিন্ন সমস্যা থেকে মুক্তি দেয়:

  • আজীবন পরিচালনা
  • পুনরাবৃত্তির দ্বিগুণ ডিফারেন্সিং

নোট করুন যে এটি vectorপারফরম্যান্স এবং ইন্টারফেস উভয় ক্ষেত্রে স্মার্ট পয়েন্টারগুলির তুলনায় উল্লেখযোগ্যভাবে ভাল ।

এখন, একটি তৃতীয় বিকল্প রয়েছে, এটি হায়ারার্কি পরিবর্তন করা। ব্যবহারকারীর আরও ভাল নিরোধক জন্য, আমি নীচের ব্যবহৃত প্যাটার্নটি কয়েকবার দেখেছি:

class IClass;

class MyClass
{
public:
  typedef enum { Var1, Var2 } Type;

  explicit MyClass(Type type);

  int foo();
  int bar();

private:
  IClass* m_impl;
};

struct IClass
{
  virtual ~IClass();

  virtual int foo();
  virtual int bar();
};

class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };

এটি বেশ সোজা, এবং Pimplএকটি Strategyপ্যাটার্ন দ্বারা সমৃদ্ধ প্রতিমাগুলির একটি ভিন্নতা ।

এটি অবশ্যই কার্যকর হয় কেবলমাত্র সেই ক্ষেত্রে যেখানে আপনি "সত্য" অবজেক্টগুলি সরাসরি পরিচালনা করতে চান না এবং এতে গভীর অনুলিপি জড়িত। সুতরাং এটি আপনার ইচ্ছা অনুযায়ী নাও হতে পারে।


4
বুস্ট রেফারেন্স এবং ডিজাইনের ধরণের জন্য আপনাকে ধন্যবাদ
ব্লুট্রিন

2

কারণ কোনও ভেক্টরকে পুনরায় আকার দেওয়ার জন্য আপনাকে ডিফল্ট কনস্ট্রাক্টর এবং শ্রেণীর আকার ব্যবহার করতে হবে, যার পরিবর্তে এটি কংক্রিটের প্রয়োজন।

অন্যান্য প্রস্তাবিত হিসাবে আপনি পয়েন্টার ব্যবহার করতে পারেন।


1

std :: ভেক্টর আপনার প্রকারটি ধারণ করতে মেমরি বরাদ্দ দেওয়ার চেষ্টা করবে। যদি আপনার ক্লাসটি নিখুঁতভাবে ভার্চুয়াল হয় তবে ভেক্টর যে শ্রেণীর বরাদ্দ করতে হবে তার আকারটি জানতে পারে না।

আমি মনে করি যে আপনার কাজের সাথে আপনি একটি সংকলন করতে সক্ষম হবেন vector<IFunnyInterface>তবে আপনি এটির ভিতরে ফানিআইপ্পলটি পরিচালনা করতে সক্ষম হবেন না। উদাহরণস্বরূপ যদি IFunnyInterface (বিমূর্ত শ্রেণি) আকার 20 এর হয় (আমি সত্যই জানি না) এবং ফানিজিম্পল 30 এর আকারের কারণ এর আরও সদস্য এবং কোড রয়েছে, আপনি 20 এর ভেক্টরের সাথে 30 ফিট করার চেষ্টা করবেন

সমাধানটি হ'ল "নতুন" এবং স্টোর পয়েন্টারগুলিতে স্তূপে মেমরি বরাদ্দ করা vector<IFunnyInterface*>


আমি ভেবেছিলাম এটিই উত্তর, তবে জিএফ উত্তর এবং অবজেক্ট স্লাইসিংয়ের সন্ধান করুন, এটি
ধারকটির

এই উত্তরে বর্ণিত হয়েছিল যে কী হবে তবে 'স্লাইসিং' শব্দটি ব্যবহার না করে, তাই এই উত্তরটি সঠিক। পিটিআরসের ভেক্টর ব্যবহার করার সময়, কোনও টুকরোগুলি ঘটবে না। এটি প্রথম স্থানে পিটিআরএস ব্যবহারের সম্পূর্ণ পয়েন্ট।
রোল

-3

আমি মনে করি যে এটি সত্যই দু: খিত সীমাবদ্ধতার মূল কারণটি সত্য যে নির্মাণকারীরা ভার্চুয়াল করতে পারে না। এরপরে সংকলক কোড তৈরি করতে পারে না যা সংকলনের সময়টি না জেনে অবজেক্টটিকে অনুলিপি করে।


4
এটি মূল কারণ নয় এবং এটি "দু: খের সীমাবদ্ধতা" নয়।

দয়া করে ব্যাখ্যা করুন কেন আপনি মনে করেন যে এটি কোনও সীমাবদ্ধতা নয়? সামর্থ্য থাকলে ভালো লাগবে। এবং প্রোগ্রামারটিতে কিছু ওভারহেড থাকে যখন সে / সে ধারকটিতে পয়েন্টার রাখতে বাধ্য হয় এবং একটি মুছে ফেলার বিষয়ে চিন্তা করে। আমি একমত যে একই ধারকটিতে বিভিন্ন আকারের জিনিস থাকা কার্যকারিতা ক্ষতিগ্রস্থ করবে।
ডেভিড গ্রুজম্যান

ভার্চুয়াল ফাংশনগুলি আপনার কাছে থাকা অবজেক্টের ধরণের ভিত্তিতে প্রেরণ করে। কনস্ট্রাক্টরদের পুরো পয়েন্টটি এখনও একটি অবজেক্ট নেই । আপনার স্থির ভার্চুয়াল ফাংশনগুলি কেন না থাকতে পারে তার সাথে সম্পর্কিত also
MSalters

আমি বলতে পারি যে শ্রেণি ধারকগুলির টেম্পলেটটির আপত্তি করার দরকার নেই, তবে একটি শ্রেণি কারখানা এবং নির্মাণকারী এটির একটি প্রাকৃতিক অংশ।
ডেভিড গ্রুজমান
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.