ফাংশন অবজেক্ট ব্যবহার করে সি ++ থ্রেড, একাধিক ডিস্ট্রাস্টারকে কীভাবে কনস্ট্রাক্টর বলা হয় না?


15

দয়া করে নীচে কোড স্নিপেট খুঁজুন:

class tFunc{
    int x;
    public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }
    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX(){ return x; }
};

int main()
{
    tFunc t;
    thread t1(t);
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

আমি যে আউটপুটটি পাচ্ছি তা হ'ল:

Constructed : 0x7ffe27d1b0a4
Destroyed : 0x7ffe27d1b06c
Thread is joining...
Thread running at : 11
Destroyed : 0x2029c28
x : 1
Destroyed : 0x7ffe27d1b0a4

আমি বিভ্রান্ত হয়ে পড়েছি কীভাবে 0x7ffe27d1b06c এবং 0x2029c28 ঠিকানার ঠিকানা সহ ডেস্ট্রাক্টরদের কল করা হয়েছিল এবং কোনও কনস্ট্রাক্টরকে ডাকা হয়নি? যেখানে যথাক্রমে প্রথম এবং শেষ নির্মাতা এবং ডেস্ট্রাক্টর হ'ল আমি তৈরি বস্তুর।


11
আপনার অনুলিপি-কর্টোর এবং মুভ-সিটিও পাশাপাশি সংজ্ঞায়িত করুন এবং উপকরণ দিন।
WhozCraig

বোঝা. যেহেতু আমি অবজেক্টটি কপির কন্সট্রাক্টরকে ডেকে পাঠিয়ে দিচ্ছি, আমি কি সঠিক? কিন্তু, মুভ কনস্ট্রাক্টর কখন ডাকা হয়?
শাহবাজ

উত্তর:


18

আপনি যন্ত্রাদি অনুলিপি-নির্মাণ এবং সরানো নির্মাণ অনুপস্থিত। আপনার প্রোগ্রামে একটি সাধারণ পরিবর্তন তার প্রমাণ প্রদান করবে যেখানে সেখানে নির্মাণ চলছে।

অনুলিপি নির্মাণকারী

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    tFunc t;
    thread t1{t};
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

আউটপুট (ঠিকানাগুলি পৃথক)

Constructed : 0x104055020
Copy constructed : 0x104055160 (source=0x104055020)
Copy constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104055020

কন্সট্রাক্টর এবং মুভ কনস্ট্রাক্টর কপি করুন

আপনি যদি একটি সরানো কর্টর সরবরাহ করেন তবে অন্য কোনও-অনুলিপিগুলির মধ্যে এটির জন্য কমপক্ষে একটির পক্ষে পছন্দ করা হবে:

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    tFunc(tFunc&& obj) : x(obj.x)
    {
        cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
        obj.x = 0;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    tFunc t;
    thread t1{t};
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

আউটপুট (ঠিকানাগুলি পৃথক)

Constructed : 0x104057020
Copy constructed : 0x104057160 (source=0x104057020)
Move constructed : 0x602000008a38 (source=0x104057160)
Destroyed : 0x104057160
Thread running at : 11
Destroyed : 0x602000008a38
Thread is joining...
x : 1
Destroyed : 0x104057020

রেফারেন্স মোড়ানো

আপনি যদি এই অনুলিপিগুলি এড়াতে চান তবে আপনি আপনার কলযোগ্যকে একটি রেফারেন্স র‌্যাপার ( std::ref) এ মোড়াতে পারেন । যেহেতু tথ্রেডিং অংশটি শেষ হওয়ার পরে আপনি ব্যবহার করতে চান , এটি আপনার পরিস্থিতির পক্ষে কার্যকর। অভ্যাসের ক্ষেত্রে কল অবজেক্টের রেফারেন্সের বিরুদ্ধে থ্রেডিংয়ের সময় আপনাকে অবশ্যই খুব সতর্কতা অবলম্বন করতে হবে , কারণ বস্তুর আজীবন কমপক্ষে যতক্ষণ থ্রেডটি রেফারেন্সটি ব্যবহার করে ততক্ষণ প্রসারিত করতে হবে।

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    tFunc(tFunc&& obj) : x(obj.x)
    {
        cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
        obj.x = 0;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    tFunc t;
    thread t1{std::ref(t)}; // LOOK HERE
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    cout<<"x : "<<t.getX()<<endl;
    return 0;
}

আউটপুট (ঠিকানাগুলি পৃথক)

Constructed : 0x104057020
Thread is joining...
Thread running at : 11
x : 11
Destroyed : 0x104057020

নোট করুন যদিও আমি কপি-সিটার এবং মুভ-সিটারের ওভারলোডগুলি রেখেছি, উভয়কেই ডাকা হয়নি, কারণ রেফারেন্সের মোড়ক এখন জিনিসটি অনুলিপি / সরানো হয়েছে; এটি উল্লেখ জিনিস নয়। এছাড়াও, এই চূড়ান্ত পদ্ধতির মাধ্যমে আপনি সম্ভবত যা খুঁজছিলেন তা সরবরাহ করে; t.xপিছনে mainআসলে, পরিবর্তিত হয় 11। এটি পূর্বের প্রচেষ্টা ছিল না। তবে এটি যথেষ্ট চাপ দিতে পারে না: এটি করতে সাবধান হন । অবজেক্টের জীবনকাল সমালোচনামূলক


সরান, এবং কিছুই না

অবশেষে, tআপনার উদাহরণ হিসাবে যেমন ধরে রাখার আপনার আগ্রহ নেই , আপনি সরল শব্দার্থক ব্যবহার করে সোজা সূত্রে প্রেরণ করতে পারেন, সেই পথ ধরেই।

#include <iostream>
#include <thread>
#include <functional>
using namespace std;

class tFunc{
    int x;
public:
    tFunc(){
        cout<<"Constructed : "<<this<<endl;
        x = 1;
    }
    tFunc(tFunc const& obj) : x(obj.x)
    {
        cout<<"Copy constructed : "<<this<< " (source=" << &obj << ')' << endl;
    }

    tFunc(tFunc&& obj) : x(obj.x)
    {
        cout<<"Move constructed : "<<this<< " (source=" << &obj << ')' << endl;
        obj.x = 0;
    }

    ~tFunc(){
        cout<<"Destroyed : "<<this<<endl;
    }

    void operator()(){
        x += 10;
        cout<<"Thread running at : "<<x<<endl;
    }
    int getX() const { return x; }
};

int main()
{
    thread t1{tFunc()}; // LOOK HERE
    if(t1.joinable())
    {
        cout<<"Thread is joining..."<<endl;
        t1.join();
    }
    return 0;
}

আউটপুট (ঠিকানাগুলি পৃথক)

Constructed : 0x104055040
Move constructed : 0x104055160 (source=0x104055040)
Move constructed : 0x602000008a38 (source=0x104055160)
Destroyed : 0x104055160
Destroyed : 0x104055040
Thread is joining...
Thread running at : 11
Destroyed : 0x602000008a38

এখানে আপনি অবজেক্টটি তৈরি হয়েছে তা দেখতে পেয়েছেন, একই-এর যথাযথ রেফারেন্সটি সোজা পাঠানো হয়েছে std::thread::thread(), যেখানে এটি আবার তার চূড়ান্ত বিশ্রামস্থানে স্থানান্তরিত হবে, সেই বিন্দু থেকে থ্রেডের মালিকানাধীন। কোনও অনুলিপি-জড়িতরা জড়িত নয়। আসল ডিটারগুলি দুটি শেল এবং চূড়ান্ত গন্তব্য কংক্রিট অবজেক্টের বিরুদ্ধে।


5

আপনার অতিরিক্ত প্রশ্ন হিসাবে মন্তব্য পোস্ট করা:

মুভ কনস্ট্রাক্টর কখন ডাকা হয়?

std::threadপ্রথমটির কনস্ট্রাক্টর তার প্রথম যুক্তির (দ্বারা decay_copy) একটি অনুলিপি তৈরি করে - সেখানে অনুলিপি নির্মাণকারীকে ডাকা হয়। (নোট করুন যে কোনও মূল্যের তর্ক হিসাবে , যেমন thread t1{std::move(t)};বা thread t1{tFunc{}};, মুভ কনস্ট্রাক্টরের পরিবর্তে কল করা হবে))

এর ফলাফলটি decay_copyএকটি অস্থায়ী যা স্ট্যাকের উপরে থাকে। যাইহোক, যেহেতু কলিং থ্রেডdecay_copy দ্বারা সঞ্চালিত হয় , এই অস্থায়ী এটি তার স্ট্যাকের উপরে থাকে এবং কনস্ট্রাক্টরের শেষে ধ্বংস হয় । ফলস্বরূপ, অস্থায়ী নিজেই নতুন তৈরি থ্রেড দ্বারা সরাসরি ব্যবহার করা যায় না।std::thread::thread

নতুন থ্রেডটিতে ফান্টরটিকে "পাস" করতে একটি নতুন অবজেক্ট অন্য কোথাও তৈরি করা দরকার এবং এটি স্থানান্তর কনস্ট্রাক্টরকে অনুরোধ করা হয়েছিল। (যদি এটি বিদ্যমান না থাকে তবে অনুলিপি নির্মাণকারীকে অনুরোধ করা হত))


নোট করুন যে আমরা আশ্চর্য হতে পারি কেন পিছিত অস্থায়ী বস্তুগতকরণ এখানে প্রয়োগ করা হয় না। উদাহরণস্বরূপ, এই লাইভ ডেমোতে , দুটির পরিবর্তে কেবলমাত্র একজন কনস্ট্রাক্টরকে ডাকা হয়। আমি বিশ্বাস করি যে সি ++ স্ট্যান্ডার্ড লাইব্রেরি বাস্তবায়নের কিছু অভ্যন্তরীণ বাস্তবায়ন বিবরণ std::threadনির্মাণকারীর জন্য প্রয়োগ করতে অপ্টিমাইজেশন বাধা দেয় ।

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