Dangling কনস্ট রেফারেন্স কীভাবে সমাধান করবেন


18

নিম্নলিখিত সংক্ষিপ্ত প্রোগ্রাম

#include <vector>
#include <iostream>

std::vector<int> someNums()
{
    return {3, 5, 7, 11};
}

class Woop
{
public:
    Woop(const std::vector<int>& nums) : numbers(nums) {}
    void report()
    {
        for (int i : numbers)
            std::cout << i << ' ';
        std::cout << '\n';
    }
private:
    const std::vector<int>& numbers;
};

int main()
{
    Woop woop(someNums());
    woop.report();
}

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


4
এটাই কৌতুকময়। আমি আপনাকে নিশ্চিত করে বলতে পারি যে কোনও সদস্যের ভেরিয়েবল কনস্ট রেফারেন্স দেওয়ার আগে আমি দুবার ভাবি। যদি সন্দেহ হয় তবে আমি এই ডেটাটি মডেল করার জন্য বিবেচনা করব যে কোনওভাবে স্মার্ট পয়েন্টার জড়িত থাকতে পারে (হয় std::unique_ptrএকচেটিয়া মালিকানা, std::shared_ptrবা ভাগ করে নেওয়া মালিকানার জন্য, বা std::weak_ptrকমপক্ষে হারিয়ে যাওয়া ডেটা সনাক্ত করতে পারে)।
শেফ

সি ++ এ, আপনার যা প্রয়োজন / ব্যবহারের প্রয়োজন নেই তার জন্য অর্থ প্রদান করবেন না। রেফারেন্সটি এখনও ব্যবহৃত / বিদ্যমান থাকা অবস্থায় রেফারেন্স করা অবজেক্টের জীবনকাল শেষ না হয় সেদিকে খেয়াল রাখা প্রোগ্রামারটির উপর নির্ভর করে। কাঁচা পয়েন্টারগুলির জন্য একই জিনিস, ... আপনি যে বৈশিষ্ট্যগুলির জন্য জিজ্ঞাসা করেছেন সেগুলি আপনার কাছে আনার জন্য স্মার্ট পয়েন্টার রয়েছে :)
ফারিয়ানোমার

2
রেফারেন্স সদস্যরা সর্বদা একটি ভুল: herbsutter.com/2020/02/23/references-simply
ম্যাক্সিম এগুরুশকিন

যদিও সংকলক সতর্ক করে না, এই বাগটি ভালগ্রাইন্ড এবং দ্বারা ক্যাচযোগ্য -fsanitize=address। আমি মনে করি না পারফরম্যান্সের ত্যাগ ছাড়াই এটি এড়াতে কোনও সেরা অনুশীলন আছে।
ks1322

উত্তর:


8

পরিস্থিতিতে যখন কোনও পদ্ধতি ফেরার পরে একটি রেফারেন্স রাখে তখন std::reference_wrapperস্বাভাবিক রেফারেন্সের পরিবর্তে এটি ব্যবহার করা ভাল ধারণা :

#include <functional>

class Woop
{
public:
    using NumsRef = ::std::reference_wrapper<const std::vector<int>>;
    Woop(NumsRef nums) : numbers_ref{nums} {}
    void report()
    {
        for (int i : numbers_ref.get())
            std::cout << i << ' ';
        std::cout << '\n';
    }
private:
    NumsRef numbers_ref;
};
  1. এটি ইতিমধ্যে ভারসাম্যগুলির সেটগুলির সাথে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ধাপে ফেলা বন্ধ করে দেয় তাই Woop (std::vector<int> const &&) = delete;আপনার পদ্ধতির জন্য কোনও অতিরিক্ত নিষিদ্ধ ওভারলোড নিয়ে কোনও মূল্য দেওয়ার দরকার নেই :
Woop woop{someNums()}; // error
woop.report();
  1. এটি লভ্যুলুগুলির অন্তর্নিহিত বাঁধাইয়ের অনুমতি দেয় যাতে এটি বিদ্যমান বৈধ আমন্ত্রণগুলি ভঙ্গ করবে না:
auto nums{someNums()};
Woop woop{nums}; // ok
woop.report();
  1. এটি কল্যাণকারীদের সুস্পষ্টভাবে বাঁধার অনুমতি দেয় যা কলার ফিরে আসার পরে রেফারেন্স রাখবে তা বোঝাতে একটি ভাল অনুশীলন:
auto nums{someNums()};
Woop woop{::std::ref(nums)}; // even better because explicit
woop.report();

10

আপনার শ্রেণিটিকে কম দুর্বল করার একটি উপায় হ'ল মুছে ফেলা কন্সট্রাক্টর যুক্ত করা যা ডান-রেফ নিতে পারে। এটি আপনার শ্রেণীর উদাহরণগুলিকে অস্থায়ীগুলিতে বাঁধাই করা থেকে বিরত করবে।

Woop(std::vector<int>&& nums)  =delete;

এই মুছে ফেলা কন্সট্রাক্টর আসলে ও / পি কোডটি সংকলন করে না, যা আপনি খুঁজছেন এমন আচরণ হতে পারে?


3

আমি অন্যান্য উত্তর এবং মন্তব্যগুলির সাথে একমত হই যে আপনার যদি ক্লাসের ভিতরে সত্যই কোনও রেফারেন্স সঞ্চয় করতে হয় তবে আপনার সাবধানতার সাথে চিন্তা করা উচিত। এবং যদি আপনি এটি করেন তবে আপনি সম্ভবত কোনও কনট ভেক্টরের পরিবর্তে (অর্থাত্ std::vector<int> const * numbers_) একটি নন-কনস্ট পয়েন্টার চাইবেন ।

যাইহোক, যদি এটি হয় তবে আমি দেখতে পাই যে অন্যান্য পোস্ট করা উত্তরগুলি বিন্দুর পাশে রয়েছে। কীভাবে Woopসেই মানগুলি কীভাবে নিজস্ব করা যায় সেগুলি তারা আপনাকে প্রদর্শন করছে ।

আপনি যদি নিশ্চিত করতে পারেন যে আপনি যে ভেক্টরটি দিয়ে গেছেন Woopতা আপনার উদাহরণকে ছাপিয়ে যাবে, তবে আপনি স্পষ্টভাবে Woopকোনও মূল্যায়ন থেকে একটি নির্মাণ নিষ্ক্রিয় করতে পারেন । এই সি ++ 11 সিনট্যাক্সটি ব্যবহার করা সম্ভব:

Woop (std::vector<int> const &&) = delete;

এখন আপনার উদাহরণ কোডটি আর সংকলন করবে না। সংকলকটি এর মতো একটি ত্রুটি দেয়:

prog.cc: In function 'int main()':
prog.cc:29:25: error: use of deleted function 'Woop::Woop(const std::vector<int>&&)'
   29 |     Woop woop(someNums());
      |                         ^
prog.cc:15:5: note: declared here
   15 |     Woop(std::vector<int> const &&) = delete;
      |     ^~~~

পিএস: আপনি সম্ভবত একটি সুস্পষ্ট নির্মাতা চান, উদাহরণস্বরূপ দেখুন স্পষ্ট কীওয়ার্ডটির অর্থ কী?


আমি আপনার উত্তর সেখানে চুরি হয়েছে বলে মনে হচ্ছে। দুঃখিত!
জেম টেলর

1

এই নির্দিষ্ট ক্ষেত্রে প্রতিরোধের জন্য, আপনি হয় পয়েন্টার নিতে বাছাই করতে পারেন (যেহেতু Weep(&std::vector<int>{1,2,3})অনুমোদিত নয়) বা আপনি একটি অ-স্থির রেফারেন্স নিতে পারেন যা কোনও অস্থায়ী ক্ষেত্রেও ত্রুটি ঘটবে।

Woop(const std::vector<int> *nums);
Woop(std::vector<int> *nums);
Woop(std::vector<int>& nums);

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

std::scoped_lockমুটেক্সের রেফারেন্স নেওয়া একটি উদাহরণ হতে পারে এবং যেখানে অনন্য / ভাগ করা / দুর্বল পিটিআরটি সত্যই কাম্য নয়। প্রায়শই std::mutexকেবলমাত্র একটি মৌলিক সদস্য বা স্থানীয় পরিবর্তনশীল হবে। আপনাকে এখনও খুব যত্নবান হতে হবে, তবে এই ক্ষেত্রে সাধারণত আয়ু নির্ধারণ করা সহজ।

std::weak_ptrমালিকানাবিহীন হওয়ার জন্য অন্য একটি বিকল্প, তবে তারপরে আপনি কলারকে ব্যবহার করতে বাধ্য করেন shared_ptr(এবং এইভাবে এছাড়াও বরাদ্দ হিপ), এবং কখনও কখনও এটি চান না।

যদি কোনও অনুলিপি ঠিক থাকে তবে তা কেবল সমস্যাটিকে এড়িয়ে চলে।

যদি Woopমালিকানা গ্রহণ করা হয় তবে তা আর-মান হিসাবে পাস এবং সরানো (এবং পুরোপুরি পয়েন্টার / রেফারেন্সের বিষয়গুলি এড়ানো), অথবা unique_ptrআপনি যদি মানটি নিজেই স্থানান্তর করতে না পারেন বা পয়েন্টারটি বৈধ থাকতে চান তবে ব্যবহার করুন।

// the caller can't continue to use nums, they could however get `numbers` from Woop or such like
// or just let Woop only manipulate numbers directly.
Woop(std::vector<int> &&nums) 
   : numbers(std::move(nums)) {}
std::vector<int> numbers;

// while the caller looses the unique_ptr, they might still use a raw pointer, but be careful.
// Or again access numbers only via Woop as with the move construct above.
Woop(std::unique_ptr<std::vector<int>> &&nums) 
    : numbers(std::move(nums)) {}
std::unique_ptr<std::vector<int>> numbers;

অথবা যদি মালিকানা ভাগ করে নেওয়া হয় তবে আপনি shared_ptrসমস্ত কিছুর জন্য ব্যবহার করতে পারেন এবং এটি চূড়ান্ত রেফারেন্স সহ মুছে ফেলা হবে তবে এটি যদি অব্যবহৃত হয় তবে অবজেক্টের লাইফ চক্রটি ট্র্যাক করে রাখতে পারে।


1

আপনি ব্যবহার করতে পারেন template programmingএবং arraysযদি আপনি এমন কোনও জিনিস রাখতে চান যা constধারক ধারণ করে । constexprকনস্ট্রাক্টরের কারণে এবং constexpr arraysআপনি অর্জন const correctnessএবং compile time execution

এখানে একটি পোস্ট যা আকর্ষণীয় হতে পারে: std :: একটি কনস্ট ভেক্টর সরান

#include <array>
#include <iostream>
#include <vector>


std::array<int,4>  someNums()
{
    return {3, 5, 7, 11};
}


template<typename U, std::size_t size>
class Woop
{
public:

template<typename ...T>
    constexpr Woop(T&&... nums) : numbers{nums...} {};

    template<typename T, std::size_t arr_size>
    constexpr Woop(std::array<T, arr_size>&& arr_nums) : numbers(arr_nums) {};

    void report()
    const {
        for (auto&& i : numbers)
            std::cout << i << ' ';
         std::cout << '\n';
    }



private: 
    const std::array<U, size> numbers;
    //constexpr vector with C++20
};

int main()
{
    Woop<int, 4> wooping1(someNums());
    Woop<int, 7> wooping2{1, 2, 3, 5, 12 ,3 ,51};

    wooping1.report();
    wooping2.report();
    return 0;
}

রান কোড

আউটপুট:

3 5 7 11                                                                                                                        
1 2 3 5 12 3 51

1
সংখ্যা হিসাবে std::arrayএটি অনুলিপি করার গ্যারান্টিযুক্ত, এমনকি অন্য কোনও চালচলনা উপলব্ধ থাকলেও। তার উপরে wooping1এবং wooping2একই ধরণের নয়, যা আদর্শের চেয়ে কম।
sp2danny

@ sp2danny আপনাকে প্রতিক্রিয়া জানাতে ধন্যবাদ এবং উভয় পয়েন্টে আপনার সাথে আমার একমত হতে হবে। ব্যবহারকারী 7860670 একটি আরও ভাল সমাধান সরবরাহ করেছেন :)
এম.ম্যাক
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.