সি ++ তে মেক_শায়ার করা এবং সাধারণ ভাগ করা_পিটারের মধ্যে পার্থক্য


275
std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));

এতে অনেক গুগল এবং স্ট্যাকওভারফ্লো পোস্ট রয়েছে তবে make_sharedসরাসরি ব্যবহারের চেয়ে কেন বেশি দক্ষ তা আমি বুঝতে পারছি না shared_ptr

কেউ কি উভয় দ্বারা তৈরি করা পদার্থের ক্রিয়াকলাপ এবং ধাপে ধাপে আমাকে ব্যাখ্যা করতে পারেন যাতে আমি কীভাবে make_sharedদক্ষ তা বুঝতে সক্ষম হব । আমি রেফারেন্সের জন্য উপরের একটি উদাহরণ দিয়েছি।


4
এটি বেশি দক্ষ নয়। এটি ব্যবহারের কারণ ব্যতিক্রম সুরক্ষার জন্য।
Yuushi

কিছু নিবন্ধ বলে, এটি ওভারহেডের কিছু নির্মাণ এড়িয়ে চলে, আপনি দয়া করে এ সম্পর্কে আরও ব্যাখ্যা করতে পারেন?
অনুপ বুচকে

16
@ ইউউশি: ব্যতিক্রমী সুরক্ষা এটি ব্যবহারের একটি ভাল কারণ, তবে এটি আরও কার্যকর।
মাইক সিমুর

3
32:15 এখানেই আমি উপরে লিঙ্ক করা ভিডিওতে তিনি শুরু করেন, যদি এটি সহায়তা করে।
ক্রিশ

4
গৌণ কোড শৈলীর সুবিধা: make_sharedআপনি লিখতে পারেন auto p1(std::make_shared<A>())এবং p1 ব্যবহার করে সঠিক ধরণ থাকবে।
ইভান ভার্জিলিভ

উত্তর:


332

পার্থক্যটি হ'ল std::make_sharedএকটি গাদা-বরাদ্দ সম্পাদন করে, যেখানে std::shared_ptrকনস্ট্রাক্টরকে কল করা দুটি সম্পাদন করে।

গাদা-বরাদ্দ কোথায় ঘটে?

std::shared_ptr দুটি সত্ত্বা পরিচালনা করে:

  • নিয়ন্ত্রণ ব্লক (মেটা ডেটা যেমন রেফ-কাউন্ট, টাইপ-মোছা মুছনকারী ইত্যাদি সঞ্চয় করে)
  • বস্তু পরিচালিত হচ্ছে

std::make_sharedনিয়ন্ত্রণ ব্লক এবং ডেটা উভয়ের জন্য প্রয়োজনীয় জায়গার জন্য একক গাদা-বরাদ্দ অ্যাকাউন্টিং সম্পাদন করে। অন্য ক্ষেত্রে, new Obj("foo")পরিচালিত ডেটার জন্য একটি গাদা-বরাদ্দের অনুরোধ করে এবং std::shared_ptrকনস্ট্রাক্টর নিয়ন্ত্রণ ব্লকের জন্য আরেকটি সঞ্চালন করে।

আরও তথ্যের জন্য, cppreferencesপ্রয়োগকরণ নোটগুলি পরীক্ষা করে দেখুন ।

আপডেট আমি: ব্যতিক্রম-সুরক্ষা

দ্রষ্টব্য (2019/08/30) : ফাংশন আর্গুমেন্টগুলির মূল্যায়ন আদেশের পরিবর্তনের কারণে সি ++ 17 থেকে এটি কোনও সমস্যা নয়। বিশেষত, অন্য কোনও যুক্তি মূল্যায়নের আগে কোনও ক্রিয়াকলাপের প্রতিটি যুক্তি পুরোপুরি কার্যকর করা প্রয়োজন।

যেহেতু ওপি জিনিসগুলির ব্যতিক্রম-সুরক্ষা দিকটি নিয়ে ভাবছে বলে মনে হচ্ছে, তাই আমি আমার উত্তর আপডেট করেছি।

এই উদাহরণটি বিবেচনা করুন,

void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }

F(std::shared_ptr<Lhs>(new Lhs("foo")),
  std::shared_ptr<Rhs>(new Rhs("bar")));

যেহেতু সি ++ স্বেচ্ছাসেবীগুলির মূল্যায়নের স্বেচ্ছাসেবী আদেশের অনুমতি দেয়, একটি সম্ভাব্য অর্ডারিং হ'ল:

  1. new Lhs("foo"))
  2. new Rhs("bar"))
  3. std::shared_ptr<Lhs>
  4. std::shared_ptr<Rhs>

এখন, ধরুন আমরা পদক্ষেপ 2 এ ছোঁড়া একটি ব্যতিক্রম পেয়েছি (উদাহরণস্বরূপ, মেমরির ব্যতিক্রমের বাইরে, Rhsনির্মাণকারী কিছু ব্যতিক্রম ছুঁড়ে দিয়েছেন)। এরপরে আমরা 1 ম পদক্ষেপে বরাদ্দ থাকা স্মৃতি হারিয়ে ফেলি কারণ কোনও কিছুই এটিকে পরিষ্কার করার সুযোগ পাবে না। এখানে সমস্যার মূল বিষয়টি হ'ল কাঁচা পয়েন্টারটি সাথে সাথে std::shared_ptrকনস্ট্রাক্টরের কাছে পৌঁছে দেওয়া হয়নি ।

এটির সমাধানের একটি উপায় হ'ল এগুলি পৃথক লাইনে করা যাতে এই স্বেচ্ছাসেবী অর্ডার না ঘটে।

auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);

অবশ্যই এটি সমাধান করার জন্য পছন্দের উপায়টি std::make_sharedপরিবর্তে ব্যবহার করা।

F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));

আপডেট দ্বিতীয়: এর অসুবিধা std::make_shared

ক্যাসির মন্তব্য উদ্ধৃত :

যেহেতু এখানে কেবলমাত্র একটি বরাদ্দ রয়েছে, যতক্ষণ না নিয়ন্ত্রণ ব্লক ব্যবহার না করা হয় ততক্ষণ পয়েন্টটির স্মৃতিশক্তি হ্রাস করা যায় না। ক weak_ptrকন্ট্রোল ব্লককে অনির্দিষ্টকালের জন্য বাঁচিয়ে রাখতে পারে।

weak_ptrএর উদাহরণগুলি কেন নিয়ন্ত্রণ ব্লককে বাঁচিয়ে রাখে?

weak_ptrপরিচালিত অবজেক্টটি এখনও বৈধ কিনা তা নির্ধারণ করার জন্য অবশ্যই একটি উপায় থাকতে হবে (উদাহরণস্বরূপ lock)। shared_ptrকন্ট্রোল ব্লকে সংরক্ষণ করা ম্যানেজড অবজেক্টের মালিকানাধীন সংখ্যার পরীক্ষা করে তারা এটি করে । ফলাফলটি হ'ল কন্ট্রোল ব্লকগুলি shared_ptrগণনা এবং অবধি বেঁচে থাকেweak_ptr গণনা দুটি হিট করে hit

আবার std::make_shared

যেহেতু std::make_sharedনিয়ন্ত্রণ ব্লক এবং পরিচালিত বস্তু উভয়ের জন্য একক হিপ-বরাদ্দ করে, তাই নিয়ন্ত্রণ ব্লক এবং পরিচালিত বস্তুর জন্য মেমরিটি স্বাধীনভাবে মুক্ত করার কোনও উপায় নেই। আমাদের নিয়ন্ত্রণ ব্লক এবং পরিচালিত অবজেক্ট উভয়ই মুক্ত না করা পর্যন্ত আমাদের অপেক্ষা করতে হবে, যা কোনও shared_ptrএস বা weak_ptrএস জীবিত না হওয়া পর্যন্ত ঘটে ।

মনে করুন পরিবর্তে আমরা নিয়ন্ত্রণ ব্লক এবং পরিচালকের মাধ্যমে newএবং shared_ptrকনস্ট্রাক্টরের জন্য দুটি হ্যাপ-বরাদ্দ সম্পাদন করেছি । তারপরে আমরা কোনও shared_ptrজীবিত না থাকা অবস্থায় পরিচালিত অবজেক্টের (সম্ভবত আগে) স্মৃতি মুক্ত করি এবং যখন কোনও weak_ptrজীবন্ত থাকে না তখন নিয়ন্ত্রণ ব্লকের (সম্ভবত পরে) মেমরি মুক্ত করি ।


53
পাশাপাশি ছোট্ট কোণার-ক্ষেত্রের ক্ষতির দিকটি উল্লেখ করা ভাল ধারণা make_shared: যেহেতু কেবলমাত্র একটি বরাদ্দ রয়েছে, নিয়ন্ত্রণ ব্লকটি আর ব্যবহার না করা অবধি পয়েন্টটির স্মৃতিশক্তি হ্রাস করা যায় না। ক weak_ptrকন্ট্রোল ব্লককে অনির্দিষ্টকালের জন্য বাঁচিয়ে রাখতে পারে।
কেসি

13
আরেকটি, আরও শৈলীগত, পয়েন্টটি হ'ল: যদি আপনি make_sharedএবং make_uniqueধারাবাহিকভাবে ব্যবহার করেন তবে আপনার নিজের কাঁচা পয়েন্টারগুলির মালিকানা নেই newএকটি কোডের গন্ধ হিসাবে প্রতিটি ঘটনাকে চিকিত্সা করতে পারে ।
ফিলিপ

5
যদি কেবলমাত্র একটি থাকে shared_ptrএবং না হয়, উদাহরণে weak_ptrকল reset()করা shared_ptrনিয়ন্ত্রণ ব্লকটিকে মুছে ফেলবে। তবে এটি নির্বিশেষে বা make_sharedব্যবহৃত হয়েছিল কিনা । ব্যবহার করা make_sharedএকটি পার্থক্য করে কারণ এটি পরিচালিত অবজেক্টের জন্য বরাদ্দকৃত মেমরির জীবনকালকে দীর্ঘায়িত করতে পারে । যখন shared_ptrগণনা 0 টি হিট হয়, পরিচালিত অবজেক্টের জন্য ডেস্ট্রাক্টরকে নির্বিশেষে ডাকা হয় make_shared, তবে এটির স্মৃতি মুক্ত করা কেবল তখনই ব্যবহার make_sharedকরা যায় না যদি ব্যবহার না করা হয়। আশা করি এটি আরও স্পষ্ট করে তুলেছে।
mpark

4
এটিও উল্লেখযোগ্য হবে যে মেক_শ্রেডগুলি "আমরা জানি আপনি কোথায় থাকবেন" অপ্টিমাইজেশানের সুবিধা নিতে পারে যা নিয়ন্ত্রণ ব্লকটিকে একটি পয়েন্টারকে আরও ছোট হতে দেয়। (বিশদগুলির জন্য, স্টিফান টি। লাভভেজের জিএন ২০১২ উপস্থাপনা প্রায় 12 মিনিটের দিকে দেখুন)) মেক-শেয়ার করা কেবলমাত্র একটি বরাদ্দ এড়ানো হয় না, এটি কম মোট স্মৃতিও বরাদ্দ করে।
জানুন সমস্ত ওয়ান্নাবে

1
@ হান্নাখলিল: আপনি সম্ভবত যা খুঁজছেন এটি কি এটি সম্ভবত রাজত্বের ...? melpon.org/wandbox/perMLink/b5EpsiSxDeEz8lGH
mpark

26

ভাগ করা পয়েন্টার উভয়ই অবজেক্টটি পরিচালনা করে এবং একটি ছোট্ট অবজেক্ট যা রেফারেন্স গণনা এবং অন্যান্য গৃহস্থালি ডেটা রাখে। make_sharedএই দুটি রাখার জন্য মেমরির একক ব্লক বরাদ্দ করতে পারে; ইতিমধ্যে বরাদ্দকৃত অবজেক্টে একটি পয়েন্টার থেকে ভাগ করে নেওয়া পয়েন্টারটি নির্মাণের জন্য রেফারেন্স গণনাটি সঞ্চয় করতে দ্বিতীয় ব্লক বরাদ্দ করতে হবে।

পাশাপাশি এই দক্ষতাটি ব্যবহার করার make_sharedঅর্থ এই যে আপনার কোনও newকাঁচা পয়েন্টার ব্যবহার করার দরকার নেই , আরও ভাল ব্যতিক্রমী সুরক্ষা দেওয়া - অবজেক্ট বরাদ্দ করার পরে তবে এটি স্মার্ট পয়েন্টারকে বরাদ্দ দেওয়ার আগে কোনও ব্যতিক্রম ছুঁড়ে ফেলার কোনও সম্ভাবনা নেই।


2
আমি আপনার প্রথম বিষয়টি সঠিকভাবে বুঝতে পেরেছি। আপনি কি দয়া করে ব্যতিক্রমী সুরক্ষা সম্পর্কে দ্বিতীয় দফায় বিস্তারিত বা কিছু লিঙ্ক দিতে পারেন?
অনুপ বুচকে 3'14

22

ইতিমধ্যে উল্লিখিতগুলির উপরে দুটি সম্ভাবনা পৃথক রয়েছে যেখানে আপনার দুটি ক্ষেত্রে সম্ভাবনা রয়েছে: যদি আপনাকে কোনও জন-সরকারী নির্মাতা (সুরক্ষিত বা ব্যক্তিগত) কল করতে হয়, তবে মেক_শ্রেড এটি অ্যাক্সেস করতে সক্ষম হতে পারে না, তবে নতুনটির বৈকল্পিকটি সূক্ষ্মভাবে কাজ করে ।

class A
{
public:

    A(): val(0){}

    std::shared_ptr<A> createNext(){ return std::make_shared<A>(val+1); }
    // Invalid because make_shared needs to call A(int) **internally**

    std::shared_ptr<A> createNext(){ return std::shared_ptr<A>(new A(val+1)); }
    // Works fine because A(int) is called explicitly

private:

    int val;

    A(int v): val(v){}
};

আমি এই সঠিক সমস্যার মধ্যে দৌড়ে এসে ব্যবহার করার সিদ্ধান্ত নিয়েছি new, অন্যথায় আমি ব্যবহার করতে পারতাম make_shared। এটি সম্পর্কে একটি সম্পর্কিত প্রশ্ন এখানে রয়েছে: stackoverflow.com/questions/8147027/…
jigglypuff

6

যদি আপনার শেয়ারড_পিটিআর দ্বারা নিয়ন্ত্রিত অবজেক্টটিতে বিশেষ মেমরি সারিবদ্ধতা প্রয়োজন হয় তবে আপনি মেক_শায়ার্ডের উপর নির্ভর করতে পারবেন না, তবে আমি মনে করি এটি ব্যবহার না করার এটিই একমাত্র ভাল কারণ।


2
আপনি যখন একটি কাস্টম মুছক নির্দিষ্ট করতে চান তখন মেক_শায়ার করা অনুপযুক্ত যেখানে দ্বিতীয় পরিস্থিতি।
জানুন সমস্ত ওয়ানবনেব

5

আমি std :: make_shared নিয়ে একটি সমস্যা দেখছি, এটি ব্যক্তিগত / সুরক্ষিত কনস্ট্রাক্টরকে সমর্থন করে না


3

Shared_ptr: দুটি গাদা বরাদ্দ সম্পাদন করে

  1. নিয়ন্ত্রণ ব্লক (রেফারেন্স গণনা)
  2. অবজেক্ট ম্যানেজ করা হচ্ছে

Make_shared: শুধুমাত্র একটি গাদা বরাদ্দ সম্পাদন করে

  1. ব্লক এবং অবজেক্টের ডেটা নিয়ন্ত্রণ করুন।

0

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

for (int k = 0 ; k < 30000000; ++k)
{
    // took more time than using new
    std::shared_ptr<int> foo = std::make_shared<int> (10);

    // was faster than using make_shared
    std::shared_ptr<int> foo2 = std::shared_ptr<int>(new int(10));
}

জিনিসটি হ'ল, মেক_শ্রেড ব্যবহার করে নতুন ব্যবহারের তুলনায় দ্বিগুণ সময় লাগল। সুতরাং, নতুন ব্যবহার করে মেক-শেয়ার্ড ব্যবহার করে একের পরিবর্তে দুটি হ্যাপ বরাদ্দ রয়েছে। হতে পারে এটি একটি নির্বোধ পরীক্ষা তবে এটি কি দেখায় না যে মেক_শ্যাড ব্যবহার করা নতুন ব্যবহারের চেয়ে বেশি সময় নেয়? অবশ্যই, আমি কেবল ব্যবহৃত সময় সম্পর্কে কথা বলছি।


4
সেই পরীক্ষা কিছুটা অর্থহীন। অপটিমাইজেশন সহ মুক্তির কনফিগারেশনে পরীক্ষাটি কি চালু হয়েছিল? এছাড়াও আপনার সমস্ত আইটেম তত্ক্ষণাত্ মুক্ত করা হয়েছে তাই এটি বাস্তবসম্মত নয়।
ফিল 1970

0

আমি মনে করি মিঃ এম্পার্কের উত্তরের ব্যতিক্রম সুরক্ষা অংশটি এখনও একটি বৈধ উদ্বেগ। শেয়ারড_পিটার তৈরি করার সময় এটির মতো: শেয়ারড_প্টর <টি> (নতুন টি), নতুন টি সফল হতে পারে, যখন ভাগ করা_প্ট্রারের নিয়ন্ত্রণ ব্লকের বরাদ্দ ব্যর্থ হতে পারে। এই দৃশ্যে, নতুন বরাদ্দকৃত টি ফাঁস হবে, যেহেতু ভাগ করা_প্টারের জানার কোনও উপায় নেই যে এটি স্থানে তৈরি হয়েছিল এবং এটি মুছে ফেলা নিরাপদ। নাকি আমি কিছু মিস করছি? আমি মনে করি না ফাংশন প্যারামিটার মূল্যায়নের কঠোর নিয়মগুলি এখানে কোনওভাবে সহায়তা করে ...

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