শ্রেণীর সদস্যদের জন্য স্মার্ট পয়েন্টার ব্যবহার করা


159

C ++ 11 এ শ্রেণীর সদস্য হিসাবে স্মার্ট পয়েন্টার ব্যবহার বুঝতে আমার সমস্যা হচ্ছে। আমি স্মার্ট পয়েন্টারগুলি সম্পর্কে অনেক কিছু পড়েছি এবং আমি মনে করি যে আমি কীভাবে unique_ptrএবং shared_ptr/ weak_ptrসাধারণভাবে কাজ করি তা বুঝতে পারি । আমি যা বুঝতে পারি না তা হ'ল আসল ব্যবহার। দেখে মনে হয় যে প্রত্যেকে unique_ptrসর্বদা পথ চলার উপায় হিসাবে ব্যবহার করার পরামর্শ দেয়। তবে আমি কীভাবে এরকম কিছু বাস্তবায়ন করব:

class Device {
};

class Settings {
    Device *device;
public:
    Settings(Device *device) {
        this->device = device;
    }

    Device *getDevice() {
        return device;
    }
};    

int main() {
    Device *device = new Device();
    Settings settings(device);
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

ধরা যাক আমি স্মার্ট পয়েন্টারগুলির সাথে পয়েন্টারগুলি প্রতিস্থাপন করতে চাই। এ unique_ptrকারণে কাজ করবে না getDevice(), তাই না? তো আমি সেই সময়টি ব্যবহার করি shared_ptrএবং কখন weak_ptr? ব্যবহারের কোন উপায় নেই unique_ptr? আমার কাছে দেখে মনে হচ্ছে বেশিরভাগ ক্ষেত্রেই shared_ptrযদি আমি সত্যিই ছোট পরিসরে একটি পয়েন্টার ব্যবহার করি না তবে আরও অর্থবোধ করে?

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> device) {
        this->device = device;
    }

    std::weak_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::weak_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

এটাই কি পথ? অনেক ধন্যবাদ!


4
এটি আজীবন, মালিকানা এবং সম্ভাব্য নালাগুলি সম্পর্কে সত্যিই পরিষ্কার হতে সহায়তা করে। উদাহরণস্বরূপ, deviceএর কনস্ট্রাক্টর পাস করার পরে settings, আপনি কি এখনও কলিং স্কোপে এটি উল্লেখ করতে সক্ষম হতে চান, বা কেবল মাধ্যমে settings? পরেরটি, unique_ptrদরকারী যদি। এছাড়াও, আপনি একটি দৃশ্যকল্প আছে যেখানে ফেরত মান না getDevice()হয় null। যদি তা না হয় তবে কেবল একটি রেফারেন্স ফিরিয়ে দিন।
কিথ

2
হ্যাঁ, shared_ptr8-10 ক্ষেত্রে একটি সঠিক। অন্যান্য 2/10 unique_ptrএবং এর মধ্যে বিভক্ত weak_ptr। এছাড়াও, weak_ptrসাধারণত বিজ্ঞপ্তি রেফারেন্স ভাঙতে ব্যবহৃত হয়; আমি নিশ্চিত না যে আপনার ব্যবহারটি সঠিক হিসাবে বিবেচিত হবে।
কলিন ডাউফিনি

2
প্রথমত, deviceডেটা সদস্যের জন্য আপনি কী মালিকানা চান ? আপনাকে প্রথমে সিদ্ধান্ত নিতে হবে।
janchopanza

1
ঠিক আছে, আমি বুঝতে পেরেছি যে কলার হিসাবে আমি তার unique_ptrপরিবর্তে ব্যবহার করতে পারি এবং কনস্ট্রাক্টরকে কল করার সময় মালিকানা ছেড়ে দিতে পারি, যদি আমি জানি তবে আমার আর এটির প্রয়োজন নেই। কিন্তু ডিজাইনার হিসাবেSettings ক্লাসের আমি জানি না কলার পাশাপাশি একটি রেফারেন্স রাখতে চান কিনা। সম্ভবত ডিভাইসটি অনেক জায়গায় ব্যবহার করা হবে। ঠিক আছে, সম্ভবত এটি আপনার পয়েন্ট। সেক্ষেত্রে আমি একমাত্র মালিক হতে পারব না এবং আমি যখন শেয়ার_পিটার ব্যবহার করব তখন আমার ধারণা। এবং: সুতরাং স্মার্ট পয়েন্টগুলি পয়েন্টারগুলি প্রতিস্থাপন করে, তবে উল্লেখগুলি নয়, তাই না?
মাইকেলেল

this-> ডিভাইস = ডিভাইস; সূচনা তালিকা ব্যবহার করুন।
নীল

উত্তর:


202

unique_ptrকারণে কাজ করবে না getDevice(), তাই না?

না, অগত্যা নয়। এখানে যা গুরুত্বপূর্ণ তা হ'ল আপনার অবজেক্টের জন্য উপযুক্ত মালিকানা নীতি নির্ধারণ করা Device, অর্থাত্ আপনার (স্মার্ট) পয়েন্টার দ্বারা নির্দেশিত বস্তুর মালিক কে হবেন।

এটি কি একাSettings বস্তুর উদাহরণ হতে চলেছে ? হবে বস্তুর ধ্বংস হয়ে যাবে স্বয়ংক্রিয়ভাবে আছে , বস্তুর ধ্বংস পরার অথবা এটি যে বস্তুর বাঁচিয়া থাকা উচিত?DeviceSettings

প্রথম ক্ষেত্রে, std::unique_ptrআপনার যা প্রয়োজন তা হ'ল এটি যেহেতু এটি Settingsপয়েন্টেড অবজেক্টটির একমাত্র (অনন্য) মালিক এবং একমাত্র অবজেক্ট যা এর ধ্বংসের জন্য দায়ী।

এই অনুমানের অধীনে, getDevice()একটি সাধারণ পর্যবেক্ষক পয়েন্টারটি ফিরিয়ে দেওয়া উচিত (পর্যবেক্ষণকারী পয়েন্টারগুলি পয়েন্টার যা পয়েন্টার অবজেক্টকে বাঁচায় না)। সরল ধরণের পর্যবেক্ষণ পয়েন্টার হ'ল কাঁচা পয়েন্টার:

#include <memory>

class Device {
};

class Settings {
    std::unique_ptr<Device> device;
public:
    Settings(std::unique_ptr<Device> d) {
        device = std::move(d);
    }

    Device* getDevice() {
        return device.get();
    }
};

int main() {
    std::unique_ptr<Device> device(new Device());
    Settings settings(std::move(device));
    // ...
    Device *myDevice = settings.getDevice();
    // do something with myDevice...
}

[ দ্রষ্টব্য 1: আপনি কেন ভাবছেন যে আমি কেন এখানে কাঁচা পয়েন্টার ব্যবহার করছি, যখন প্রত্যেকেই বলতে থাকে যে কাঁচা পয়েন্টারগুলি খারাপ, অনিরাপদ এবং বিপজ্জনক। আসলে, এটি একটি মূল্যবান সতর্কতা, তবে এটি সঠিক প্রসঙ্গে রাখা গুরুত্বপূর্ণ: ম্যানুয়াল মেমরি পরিচালনার জন্য কাঁচা পয়েন্টারগুলি খারাপ হয় , অর্থাত্ বণ্টন মাধ্যমে বস্তু deallocating newএবং delete। রেফারেন্স শব্দার্থবিজ্ঞান অর্জন এবং অ-মালিকানা, পর্যবেক্ষক পর্যবেক্ষকগুলির কাছাকাছি যাওয়ার জন্য যখন বিশুদ্ধরূপে ব্যবহার করা হয় তখন কাঁচা পয়েন্টারগুলিতে অভ্যন্তরীণভাবে বিপজ্জনক কিছু নেই, তবে এই কারণে যে কোনও ব্যক্তির ঝুঁকিপূর্ণ পয়েন্টারকে অবজ্ঞা না করার জন্য যত্ন নেওয়া উচিত except - শেষ দ্রষ্টব্য 1 ]

[ দ্রষ্টব্য 2: যেমন মন্তব্যগুলিতে প্রকাশিত হয়েছে, এই বিশেষ ক্ষেত্রে যেখানে মালিকানা অনন্য এবং মালিকানাধীন বস্তু সর্বদা উপস্থিত থাকার গ্যারান্টিযুক্ত (যেমন অভ্যন্তরীণ ডেটা সদস্য deviceকখনও হবে না nullptr), ফাংশন getDevice()পারে (এবং হতে পারে)) পয়েন্টারের চেয়ে রেফারেন্স ফিরিয়ে দিন। যদিও এটি সত্য, আমি এখানে একটি কাঁচা পয়েন্টার ফিরিয়ে আনার সিদ্ধান্ত নিয়েছি কারণ আমি বোঝাতে চেয়েছিলাম যে এটি একটি সংক্ষিপ্ত উত্তর যা যে কেসটি সাধারণ deviceহতে পারে যেখানে হতে পারেnullptr , এবং যে কাঁচা পয়েন্টারগুলি সেগুলির জন্য ব্যবহার না করে ততক্ষণ ঠিক আছে show ম্যানুয়াল মেমরি পরিচালনা - শেষ দ্রষ্টব্য 2 ]


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

এটি এমন একটি বিষয় যা কেবলমাত্র আপনার প্রোগ্রামের ডিজাইনার হিসাবে আপনি বলতে পারেন; আপনি যে উদাহরণ সরবরাহ করেছেন তা থেকে, আমার পক্ষে বলা শক্ত যে এই ঘটনাটি কিনা তা নয় or

এটি নির্ধারণ করার জন্য, আপনি নিজেকে জিজ্ঞাসা করতে পারেন Settingsযে এটি ব্যতীত অন্য কোনও অবজেক্ট রয়েছে কিনা Deviceযতক্ষণ না তারা কেবলমাত্র নিষ্ক্রিয় পর্যবেক্ষক না হয়ে বস্তুটির পয়েন্টার ধরে রাখে ততক্ষণ তাকে জীবন্ত রাখার অধিকার রয়েছে। যদি তা প্রকৃতপক্ষে হয় তবে আপনার প্রয়োজন একটি ভাগ করা মালিকানা নীতি , যা অফারটি তা std::shared_ptr:

#include <memory>

class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(std::shared_ptr<Device> const& d) {
        device = d;
    }

    std::shared_ptr<Device> getDevice() {
        return device;
    }
};

int main() {
    std::shared_ptr<Device> device = std::make_shared<Device>();
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice = settings.getDevice();
    // do something with myDevice...
}

লক্ষ্য করুন weak_ptr একটি পর্যবেক্ষক পয়েন্টার, কোনও নিজস্ব পয়েন্টার নয় - অন্য কথায়, যদি নির্দেশিত বস্তুতে অন্য সমস্ত নিজস্ব পয়েন্টারগুলি সুযোগের বাইরে চলে যায় তবে এটি পয়েন্ট পয়েন্টটিকে জীবিত রাখে না।

সুবিধা weak_ptrনিয়মিত কাঁচা পয়েন্টার উপর আপনি নিরাপদে বলতে পারেন কিনা তা ব্যবহারকারীকে weak_ptrহয় আনত বা না (অর্থাত এটির একটি বৈধ বস্তু নির্দেশ করা হয় কিনা, অথবা যদি বস্তুর আদতে ধ্বংস হয়ে গেছে উল্লেখ)। এটি বস্তুটিতে expired()সদস্য ফাংশন কল করে কাজটি করা যেতে পারে weak_ptr


4
@ এলকেকে: হ্যাঁ, ঠিক আছে। এ weak_ptrসর্বদা কাঁচা পর্যবেক্ষণ পয়েন্টারগুলির বিকল্প। এটি একটি অর্থে নিরাপদ, কারণ আপনি এটি পরীক্ষা-নিরীক্ষার আগে এটি ঝুঁকছে কিনা তা পরীক্ষা করতে পারেন তবে এটি কিছুটা ওভারহেডের সাথে আসে। আপনি যদি সহজেই গ্যারান্টি দিতে পারেন যে আপনি কোনও ঝুঁকিপূর্ণ পয়েন্টারকে অবজ্ঞা করছেন না, তবে কাঁচা পয়েন্টার পর্যবেক্ষণ করে আপনার ভাল হওয়া উচিত
অ্যান্ডি প্রল

6
প্রথম ক্ষেত্রে এটি সম্ভবত আরও ভাল হবে যে getDevice()কোনও রেফারেন্স ফেরত দেওয়া উচিত , না কেন? সুতরাং কলারের জন্য চেক করতে হবে না nullptr
ভোবজেক্ট

5
@ চিকো: আপনার অর্থ কী তা নিশ্চিত নয়। auto myDevice = settings.getDevice()ধরনের একটি নতুন দৃষ্টান্ত তৈরি করবে Deviceনামক myDeviceএবং এটি যে রেফারেন্স দ্বারা রেফারেন্সড এক থেকে কপি-গঠন করা getDevice()আয়। আপনি যদি myDeviceএকটি রেফারেন্স হতে চান , আপনার করা দরকার auto& myDevice = settings.getDevice()। সুতরাং আমি যদি কিছু মিস করছি না, তবে আমরা ব্যবহার না করে আমাদের একই পরিস্থিতিতে ফিরে আসছি auto
অ্যান্ডি প্রোল

2
@ পারফরম্যান্স: যেহেতু আপনি অবজেক্টের মালিকানা ছেড়ে দিতে চান না - unique_ptrক্লায়েন্টের কাছে কোনও পরিবর্তনযোগ্য হস্তান্তর করার ফলে ক্লায়েন্ট এটি থেকে সরিয়ে নেওয়ার সম্ভাবনাটি উন্মুক্ত করে, সুতরাং মালিকানা অর্জন এবং আপনাকে একটি নাল (অনন্য) পয়েন্টার সহ রেখে যায়।
অ্যান্ডি প্রোল 21

7
@ পারফরম্যান্স: যদিও এটি কোনও ক্লায়েন্টকে চলাচল করতে বাধা দিতে পারে (যদি না ক্লায়েন্ট পাগল বিজ্ঞানী না হন তবে const_cast) আমি ব্যক্তিগতভাবে এটি করব না n't এটি বাস্তবায়নের বিশদটি প্রকাশ করে, অর্থাত্ মালিকানা অনন্য এবং এ এর ​​মাধ্যমে উপলব্ধি unique_ptr। আমি জিনিসগুলি এইভাবে দেখছি: আপনি যদি / মালিকানা ফেরত / ফেরত নিতে চান তবে একটি স্মার্ট পয়েন্টারটি পাস / ফেরত দিন ( unique_ptrবা shared_ptr, মালিকানার ধরণের উপর নির্ভর করে)। আপনি যদি মালিকানাটি পাস / ফেরত দিতে চান না / থাকেন তবে একটি (সঠিকভাবে constযোগ্য) পয়েন্টার বা রেফারেন্স ব্যবহার করুন, বেশিরভাগ ক্ষেত্রে যুক্তিটি বাতিল হতে পারে কি না তার উপর নির্ভর করে।
অ্যান্ডি প্রোল

0
class Device {
};

class Settings {
    std::shared_ptr<Device> device;
public:
    Settings(const std::shared_ptr<Device>& device) : device(device) {

    }

    const std::shared_ptr<Device>& getDevice() {
        return device;
    }
};

int main()
{
    std::shared_ptr<Device> device(new Device());
    Settings settings(device);
    // ...
    std::shared_ptr<Device> myDevice(settings.getDevice());
    // do something with myDevice...
    return 0;
}

week_ptrশুধুমাত্র রেফারেন্স লুপের জন্য ব্যবহৃত হয়। নির্ভরতা গ্রাফ অবশ্যই অ্যাক্লিক্লিডাইরেক্টেড গ্রাফ হতে হবে। ভাগ করা পয়েন্টারে 2 টি রেফারেন্স গণনা রয়েছে: shared_ptrs এর জন্য 1 , এবং সমস্ত পয়েন্টার ( shared_ptrএবং weak_ptr) এর জন্য 1 । সমস্ত shared_ptrগুলি মুছে ফেলা হলে, পয়েন্টারটি মোছা হয়। যখন থেকে পয়েন্টারটি প্রয়োজন হয় weak_ptr, lockপয়েন্টারটি উপস্থিত থাকলে এটি ব্যবহার করা উচিত।


সুতরাং আমি যদি আপনার উত্তরটি সঠিকভাবে বুঝতে পারি, স্মার্ট পয়েন্টারগুলি কাঁচা পয়েন্টারগুলি প্রতিস্থাপন করে, তবে প্রয়োজনীয় রেফারেন্সগুলি নয়?
মাইকেলেল

আসলে একটি এ দুটি রেফারেন্স গণনা আছে shared_ptr? আপনি দয়া করে কেন ব্যাখ্যা করতে পারেন? যতদূর আমি বুঝতে পেরেছি, weak_ptrএটি গণনা করতে হবে না কারণ এটি কেবলমাত্র একটি নতুন তৈরি করে shared_ptrযখন বস্তুর উপর অপারেশন করা হয় (যদি অন্তর্নিহিত অবজেক্টটি এখনও বিদ্যমান থাকে)।
বিজন পোলাক্স

@ বিজার্নপ্লেক্স: আমি আপনার জন্য একটি সংক্ষিপ্ত উদাহরণ তৈরি করেছি: লিঙ্ক । আমি সমস্ত কিছু কেবল অনুলিপি নির্মাণকারী এবং বাস্তবায়ন করি নি lockবুস্ট সংস্করণটিও রেফারেন্স গণনাতে নিরাপদ থ্রেড ( deleteকেবল একবারেই বলা হয়)।
নাস্ত্তা

@ নাজতা: আপনার উদাহরণটি দেখায় যে এটি দুটি রেফারেন্স গণনা ব্যবহার করে এটি বাস্তবায়ন করা সম্ভব তবে আপনার উত্তর থেকে বোঝা যায় যে এটি প্রয়োজনীয় , যা আমি বিশ্বাস করি না এটি। আপনি দয়া করে আপনার উত্তরে এটি পরিষ্কার করতে পারেন?
বিজন পোলাক্স

1
@ বিজার্নপ্লেক্স, weak_ptr::lock()অবজেক্টটির মেয়াদ শেষ হয়ে গেছে কিনা তা জানার জন্য এটি অবশ্যই "কন্ট্রোল ব্লক" পরিদর্শন করতে হবে যা এতে প্রথম রেফারেন্স গণনা এবং অবজেক্টের পয়েন্টার রয়েছে, সুতরাং নিয়ন্ত্রণ ব্লকটি অবশ্যই ধ্বংস হবে না যখন সেখানে weak_ptrএখনও অবৈধ ব্যবহার রয়েছে, সুতরাং weak_ptrঅবজেক্টের সংখ্যা অবশ্যই ট্র্যাক করতে হবে, এটিই দ্বিতীয় রেফারেন্স গণনাটি করে। প্রথম রেফ গণনা শূন্যে নেমে গেলে বস্তুটি ধ্বংস হয়ে যায়, দ্বিতীয় রেফের গণনা শূন্যে নেমে গেলে কন্ট্রোল ব্লক নষ্ট হয়ে যায়।
জোনাথন ওয়েকেলি
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.