উত্পন্ন শ্রেণি যদি কোনও কাঁচা গতিশীল মেমরি বরাদ্দ না করে তবে এখানে বেস শ্রেণীর ভার্চুয়াল ডেস্ট্রাক্টর থাকা দরকার কেন?


12

নিম্নলিখিত কোডের ফলে মেমরি ফাঁস হয়:

#include <iostream>
#include <memory>
#include <vector>

using namespace std;

class base
{
    void virtual initialize_vector() = 0;
};

class derived : public base
{
private:
    vector<int> vec;

public:
    derived()
    {
        initialize_vector();
    }

    void initialize_vector()
    {
        for (int i = 0; i < 1000000; i++)
        {
            vec.push_back(i);
        }
    }
};

int main()
{
    for (int i = 0; i < 100000; i++)
    {
        unique_ptr<base> pt = make_unique<derived>();
    }
}

এটি আমার কাছে খুব একটা অর্থবহ হয়নি, যেহেতু শ্রেণি থেকে প্রাপ্ত কোনও কাঁচা গতিশীল মেমরি বরাদ্দ করে না, এবং অনন্য_আপনার নিজেই ডিলোকেট করে। আমি পেয়েছি যে ক্লাস বেসের অন্তর্নিহিত ডেস্ট্রাক্টরকে ডারভেডের পরিবর্তে ডাকা হচ্ছে, তবে কেন এখানে সমস্যা হচ্ছে তা আমি পাই না। যদি আমি উদ্ভূতগুলির জন্য একটি স্পষ্টত ডেস্ট্রাক্টর লিখি তবে আমি ভিসির জন্য কিছুই লিখব না।


4
আপনি ধরেই নিচ্ছেন যে কোনও ম্যানুয়ালি লিখে থাকলেই কেবল একজন ডেস্ট্রাক্টর উপস্থিত থাকে; এই অনুমানটি ত্রুটিযুক্ত: ভাষাটি এমন একটি সরবরাহ করে ~derived()যা ভিসির ডেস্ট্রাক্টরকে প্রতিনিধি করে। বিকল্পভাবে, আপনি ধরে নিচ্ছেন যে unique_ptr<base> ptউদ্ভূত ধ্বংসকারীকে জানতে পারে know ভার্চুয়াল পদ্ধতি ছাড়া এটি হওয়া যায় না। যদিও কোনও অনন্য_পিটারকে কোনও মুছে ফাংশন দেওয়া যেতে পারে যা কোনও রানটাইম উপস্থাপনা ছাড়াই একটি টেম্পলেট প্যারামিটার এবং এই বৈশিষ্ট্যটি এই কোডটির জন্য কোনও কাজ নয়।
আমন

কোডটি সংক্ষিপ্ত করার জন্য আমরা কি একই লাইনে বন্ধনী স্থাপন করতে পারি? এখন আমাকে স্ক্রোল করতে হবে।
লাইক

উত্তর:


14

সংকলক যখন এর ডেস্ট্রাক্টরের delete _ptr;অভ্যন্তরের অভ্যন্তরীণ সম্পাদন করতে যায় unique_ptr(সেখানে _ptrপয়েন্টারটি কোথায় সঞ্চিত আছে unique_ptr) তখন এটি দুটি জিনিস অবিকল জানবে:

  1. মুছে ফেলা জিনিসটির ঠিকানা।
  2. পয়েন্টারের ধরণ যা _ptr। যেহেতু পয়েন্টারটি ভিতরে রয়েছে unique_ptr<base>, এর অর্থ _ptrপ্রকারের base*

এই সমস্ত সংকলক জানেন। সুতরাং, প্রদত্ত যে এটি কোনও ধরণের অবজেক্ট মুছে ফেলছে base, তা অনুরোধ করবে ~base()

সুতরাং ... এটি যে অংশটি এটি derviedবস্তুটিকে ধ্বংস করে যেখানে এটি আসলে দেখায়? কারণ যদি সংকলকটি জানে না যে এটি কোনওটি ধ্বংস করছে derived, তবে এটি একেবারেই derived::vec বিদ্যমান তা জানেন না, এটি ধ্বংস করা উচিত alone সুতরাং আপনি অবজেক্টটিকে তার অর্ধেকটা অবিক্রিত রেখে ভেঙে ফেলেছেন।

সংকলকটি ধরে নিতে পারে না যে কোনও base*ধ্বংস হওয়া আসলেই একটি derived*; সর্বোপরি, প্রাপ্ত ক্লাসের সংখ্যা হতে পারে base। এটি কীভাবে জানবে যে এই বিশেষতটি base*আসলে কোন ধরণের নির্দেশ করে?

সংকলকটি যা করতে হবে তা কল করার জন্য সঠিক ডেস্ট্রাক্টর খুঁজে বের করা (হ্যাঁ, derivedএকজন ডেস্ট্রাক্টর রয়েছে you আপনি যদি ডেস্ট্রাক্টর না করেন তবে প্রতিটি ক্লাসেরই= delete ডেস্ট্রাক্টর থাকে, আপনি একটি লিখেন বা না লিখুন)। এটি করার জন্য, এটি আহ্বান করতে ডিস্ট্রাক্টর কোডের সঠিক ঠিকানা পেতে তথ্য সঞ্চিত কিছু তথ্য ব্যবহার করতে হবে, তথ্যটি যা প্রকৃত শ্রেণীর নির্মাতা দ্বারা নির্ধারিত রয়েছে। তারপরে এটি সম্পর্কিত ক্লাসের ঠিকানায় পয়েন্টারে রূপান্তর করতে এই তথ্যটি ব্যবহার করতে হবে (যা অন্য কোনও ঠিকানায় হতে পারে বা নাও হতে পারে। হ্যাঁ, সত্যিই)। এবং তারপরে এটি ধ্বংসকারীকে ডাকে।basebase*derived

আমি যে প্রক্রিয়াটি শুধু বর্ণনা করেছি? একে সাধারণত "ভার্চুয়াল প্রেরণ" বলা হয়: ওরফে, আপনি virtualযখন কোনও বেস ক্লাসের পয়েন্টার / রেফারেন্স থাকবেন তখন চিহ্নিত ফাংশনটি কল করার সময় যে কোনও কিছু ঘটে ।

আপনার যদি সমস্ত কিছু বেস ক্লাস পয়েন্টার / রেফারেন্স হয় আপনি যদি ডেরিভেড ক্লাস ফাংশনটি কল করতে চান তবে সেই ফাংশনটি অবশ্যই ঘোষণা করতে হবে virtual। ধ্বংসকারীরা এ ক্ষেত্রে মৌলিকভাবে আলাদা নয়।


0

উত্তরাধিকার

উত্তরাধিকারের পুরো বিষয়টি হ'ল অনেকগুলি বাস্তবায়নের মধ্যে একটি সাধারণ ইন্টারফেস এবং প্রোটোকল ভাগ করে নেওয়া যে কোনও উদ্ভূত শ্রেণীর উদাহরণটি অন্য কোনও উদ্ভূত প্রকারের থেকে অন্য কোনও ঘটনার সাথে একইভাবে বিবেচনা করা যেতে পারে।

সি ++ এ উত্তরাধিকার এছাড়াও এর সাথে বাস্তবায়নের বিশদ নিয়ে আসে, ডেস্ট্রাক্টরকে ভার্চুয়াল হিসাবে চিহ্নিত (বা চিহ্নিত না করে) এমনই একটি বাস্তবায়ন বিশদ।

ফাংশন বাইন্ডিং

এখন যখন কোনও ফাংশন, বা এর কোনও বিশেষ ক্ষেত্রে যেমন কনস্ট্রাক্টর বা ডেস্ট্রাক্টর ডাকা হয় তখন সংকলকটিকে অবশ্যই বেছে নিতে হবে কোন ফাংশন বাস্তবায়ন বোঝানো হয়েছিল। তারপরে এটি অবশ্যই মেশিন কোড উত্পন্ন করবে যা এই উদ্দেশ্যটি অনুসরণ করে।

এটির কাজ করার সহজতম উপায় হ'ল সংকলনের সময় ফাংশনটি নির্বাচন করা এবং পর্যাপ্ত পরিমাণে মেশিন কোড নির্গত করা যাতে কোনও মান নির্বিশেষে, যখন কোডটির এই অংশটি কার্যকর করে, এটি সর্বদা ফাংশনটির জন্য কোড চালায়। উত্তরাধিকার বাদে এটি দুর্দান্ত কাজ করে।

আমাদের যদি কোনও ফাংশন সহ বেস ক্লাস থাকে (কনস্ট্রাক্টর বা ডেস্ট্রাক্টর সহ কোনও ফাংশন হতে পারে) এবং আপনার কোডটিতে কোনও ফাংশন কল করে, এর অর্থ কী?

আপনার উদাহরণ থেকে গ্রহণ, যদি আপনি নামক initialize_vector()কম্পাইলার যদি সত্যিই পাওয়া বাস্তবায়ন কল বোঝানো সিদ্ধান্ত নিতে হয়েছে Base, অথবা বাস্তবায়ন পাওয়া Derived। এটি সিদ্ধান্ত নেওয়ার দুটি উপায় রয়েছে:

  1. প্রথম সিদ্ধান্ত নিতে যে কারণ আপনি একটি থেকে বলা হয় Baseটাইপ, আপনি বাস্তবায়ন বোঝানো Base
  2. দ্বিতীয়টি সিদ্ধান্ত নিতে হয় যেহেতু টাইপ করা মূল্যে সঞ্চিত মানের রানটাইম টাইপ Baseহতে পারে Base, বা Derivedকোন কল করতে হবে তা সিদ্ধান্ত নেওয়ার সময় রানটাইমে করা উচিত (প্রতিবার যখন এটি বলা হয়)।

এই মুহূর্তে সংকলক বিভ্রান্ত, উভয় বিকল্প সমানভাবে বৈধ। এই virtualমিশ্রণে আসে যখন । যখন এই কীওয়ার্ডটি উপস্থিত থাকে তখন কোডটি একটি আসল মান সহ চলমান অবধি সমস্ত সম্ভাব্য প্রয়োগের মধ্যে সিদ্ধান্ত বিলম্ব করে সংকলক পিক্স বিকল্প 2 2 যখন এই কীওয়ার্ডটি অনুপস্থিত থাকে তখন সংকলকটি বিকল্পটি বেছে নেয় 1 কারণ অন্যথায় এটি স্বাভাবিক আচরণ।

ভার্চুয়াল ফাংশন কলের ক্ষেত্রে সংকলকটি এখনও বিকল্প 1 বেছে নিতে পারে। তবে কেবল যদি এটি প্রমাণ করতে পারে যে এটি সর্বদা ক্ষেত্রে থাকে।

নির্মাণকারী এবং ধ্বংসকারী

সুতরাং কেন আমরা একটি ভার্চুয়াল কনস্ট্রাক্টর নির্দিষ্ট করে না?

আরো intuitively, কিভাবে কম্পাইলার নির্মাণকারী এর অভিন্ন বাস্তবায়নের মধ্যে বাছাই করবে Derivedএবং Derived2? এটি বেশ সহজ, এটি পারে না। পূর্ব-বিদ্যমান কোনও মান নেই যা থেকে সংকলকটি জানতে পারে যে আসলে কী ছিল। পূর্ব-অস্তিত্বের মান নেই কারণ এটি নির্মাণকারীর কাজ।

তাহলে কেন আমাদের ভার্চুয়াল ডেস্ট্রাক্টর নির্দিষ্ট করা দরকার?

আরও স্বজ্ঞাতভাবে কীভাবে সংকলক Baseএবং এর জন্য বাস্তবায়নগুলির মধ্যে নির্বাচন করবেন Derived? এগুলি কেবল ফাংশন কল, সুতরাং ফাংশন কল আচরণ ঘটে। কোনও ঘোষিত ভার্চুয়াল ডেস্ট্রাক্টর ছাড়াই সংকলক Baseমান রানটাইম টাইপের নির্বিশেষে সরাসরি ডেস্ট্রাক্টরের সাথে আবদ্ধ করার সিদ্ধান্ত নেবে ।

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

সি ++ তে কোনও বেস বা ইন্টারফেস ধরণের ঘোষণা করার একমাত্র সঠিক উপায় হ'ল ভার্চুয়াল ডেস্ট্রাক্টর ঘোষণা করা, যাতে সঠিক ধরণের ডেস্ট্রাক্টরকে সেই ধরণের ধরণের শ্রেণিবিন্যাসের কোনও নির্দিষ্ট উদাহরণের জন্য ডাকা হয়। এটি উদাহরণটির সবচেয়ে জ্ঞান সহ ফাংশনটিকে সেই দৃষ্টিকোণটি সঠিকভাবে পরিষ্কার করতে দেয়।

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