رفتن به مطلب
جامعه‌ی برنامه‌نویسان مُدرن ایران

فرهاد شیری

مدیران مرجع
  • تعداد ارسال ها

    173
  • تاریخ عضویت

  • روز های برد

    49

آخرین بار برد فرهاد شیری در 21 آبان

فرهاد شیری یکی از رکورد داران بیشترین تعداد پسند مطالب است !

اعتبار در سایت

184 عالی

5 دنبال کننده

درباره فرهاد شیری

توسعه‌ دهنده بَک اِند
توسعه‌ دهنده فرانت اِند
اساتید
  • تاریخ تولد 20 مهر 1360

موقعیت

  • شهر
    تهران

آخرین بازدید کنندگان نمایه

887 بازدید کننده نمایه
  1. فرهاد شیری

    سلام خوش آمدید برای برنامه نویسی اندروید، بهترین روش یادگیری آموزش زبان پایه سیستم عامل اندروید که همان جاوا می باشد را باید یاد بگیرید. بنابراین برای شروع حتما یک دوره ویدئویی مقدماتی از Java SE و بعد یک دوره مقدماتی اندروید برای شروع خیلی خوب است. برای تهیه آموزش های با کیفیت و معتبر بهتر هست که از سیستم آموزشی فانوکس استفاده کنید. سیستم آموزشی فانوکس، موفق باشید
  2. پچ کردن dll چه ربطی به preloader library linker می تونه داشته باشه؟ این مطلبی که دربالا بحث شده هیچ ربطی به تزریق استاتیک کد اسمبلی نداره! و یک سوال دیگه ؟ منظورتون از push کردن تمام رجیسترها چی؟ چطوری میخواهید رجیسترها را push کنید وبعدهم همه را pop کنید! بنظرم همانطور که کل این مقاله را اشتباه متوجه شدید ظاهرا در برنامه نویسی اسمبلی و روش های پچ کردن و تزریق کد هم دچار اشکال هستید. به نظرم دوست گرامی بهتره که هر مطلبی که مطالعه میکنیم را بیان نکنیم، مگه با دلیل و منطقی که بتونیم براش مثالی هم بزنیم. اصلا فلسفه هوک کردن api های ویندوز چیزه دیگه ای هست که همانطور که قبلا گفتم هیچ ربطی به این مقاله نداره!
  3. با سلام همانطور که می دانید وقتی یک متغیر کلاس اتوماتیک را به صورت استاتیک در بدنه یک تابع تعریف میکنید و درصورتی که این تابع به صورت external تعریف شده باشه، قطعا با هر بار فراخوانی این تابع مقدار متغیر استاتیک فقط یکبار در اولین فراخوانی مقدار دهی خواهد شد و در فراخوانی های بعدی آخرین مقدار خود را حفظ خواهد کرد. بنابراین نکته ای که میتونه خیلی کاربردی باشه این هست که رفتار کامپایلر با متغیرهای استاتیک درون یک تابع template براساس آرگومان template تابع متفاوت خواهد بود، یعنی کامپایلر نوع استاتیک را براساس نوع آرگومان تابع template تعیین میکند و در section .rodata چندین نگارش از متغیر استاتیک را می نویسد، پس نتیج میگیرم که متغیرهای استاتیکی که در بدنه یک تابع template تعریف شده اند برای تابع یکتا نیستند و مقادیر مختلفی برای تابع ساخته شده است. به همین علت در زمان استفاده از یک تابع template و متغیرهای استاتیک حتما به نوع آرگومان ارسالی به تابع دقت داشته باشید که نوع استاتیک تعریف شده در بدنه تابع میتواند مقادیر مختلفی را به شما نشان دهد و به همین سادگی گرفتار یکی از باگهای منطقی در زمان اجرا شوید. با توضیحاتی که ارائه شد به مثال زیر توجه کنید... template <typename T> void ef1() { static int stat = 0; std::cout << stat++; }; و اگر من دستورات زیر را فراخوانی کنم... ef1<int>(); ef1<int>(); ef1<int>(); با اجرای کد بالا قطعا مقادیر خروجی 012 را خواهم داشت. ولی اگر از دستورات زیر استفاده کنم... ef1<int*>(); ef1<int*>(); شاید انتظار داشتید که خروجی برنامه بعد از اجرای دستورات بالا مقدار 34 را نمایش میداد ولی خیر خروجی برنامه 01 خواهد بود. ویا اگر... ef1<int&>(); ef1<int&>(); بازهم خروجی 01 خواهد بود. ویا حتی اگر... ef1<const int>(); ef1<const int>(); وبازهم خروجی 01 خواهد بود. پس حتما در زمان طراحی شی گرائی و چند ریختی در زمان اجرا در هنگام تعریف کلاسها ویا توابعی که به صورت الگو هستند، اگر از نوع استاتیک استفاده میکنید دقت لازم را داشته باشید. البته یک نکته ای که زبانی سی++ را از سایر زبانها متمایز میکند، همین تکنیک هست که به لطف وجود همچنین تکنیکی شما امکان طراحی یک کلاس template که به صورت singletone باشد را خواهید داشت، یعنی کلاسی که برای انواع آبجکتهای شما یک رفرنس یکتا ایجاد کند تا مجبور نباشید که برای هر آبجکتی که قصد استفاده از الگوی singletone را برایش داشتید جداگانه یک کلاس بنویسید. به همین علت هست که زبانهای مثل جاوا ویا سی شارپ امکان ایجاد یک کلاس ژنریک سینگلتون را ندارند.! (البته در زبان سی شارپ می توان کلاس ژنریک سینگلتون تعرف کرد ولی به علت قوانین مدیریت حافظه ای که در سی شارپ وجود دارد، نمی توان تضمین کرد که یک کلاس ژنریک سینگلتون واقعا به یک کپی اشاره داشته باشد.)
  4. معمولا یکی از کابوس هایی که میتونه برای کسانی که از VmWare استفاده میکنند و یک توزیع لینوکس دارند که باهاش یک نرم افزار را توسعه میدهند، این هست که به هردلیلی کرنل لینوکس کرش کنه و دیگه بوت نشه در چنین شرایطی اگر اسنپ شات به روزی از سیستم عامل هاست خودتون نداشته باشید ویا یک image بک آپ درستی نداشته باشید، شاید به فکر این بیوفتید که هر طور شده بتونید جدول fat لینوکس را بازیابی کنید تا حداقل تو یک مد متنی به سورس فایلها که همانا ارزشمندترین دارائی ماهستند، دسترسی داشته باشید. خوب این سناریوی خوبی ولی تعداد ابزارهایی که بتونن این فرمت ex2fs را بخونن که بتونن فایلهای سورس شما را بازیابی کنند خیلی کم هستند و اکثر ابزارهایی هم که در اینترنت هستند 99 درصدشون سرکاری هستند. بنابراین اگر شما هم برای لینوکس هاست خودتون در برنامه وی ام ویر هارد دیسک تون را از نوع vmdk تعریف کردید و به صورت ex2fs پارتیشن کردید باید بگم خوشبختانه یک راه یکم سخت هست که بتونید در مواقع اضطراری سورس های خودتون را ریستور کنید. البته توجه داشته باشید که با روش زیر فقط میتوانید اطلاعات را بخونید چیزی نمی توانید در دیسک vmdk بنویسید.! بنابراین به یک برنامه به نام DiskInternals Linux Reader احتیاج دارید که باید نصب کنید! والبته برای سناریویی هم که در بالا اشاره شد به برنامه winhex هم نیاز دارید که در پست قبلی توضیح داده شده است. پس بعد از نصب برنامه DiskInternals کافی که فایل vmdk لینوکس خودتون را انتخاب کنید، سپس با کلیک سمت راست از منوی ظاهر شده گزینه create image را انتخاب کنید و بعد از انتخاب یک نام فایل مشخص برای دیسک ایمیج خودتون طبق تصویر بعدی عمل کنید... و بعد از اتمام ساخت فایل image disk برنامه WinHex را اجرا کنید و طبق توضیحات زیر عمل کنید... ابتدا فایل مورد نظر را باز کنید.. سپس با استفاده از گزینه ای که در تصویر پست قبلی مشخص شده فایل را بارگذاری کنید...
  5. بله منهم موافقم تجربه نشون داده این که می فرمائید درسته و در توسعه محصول خیلی کمک میکنه! ارداتمندیم
  6. فرهاد شیری

    اصولا همیشه اعتقاد دارم که کسی که یک مطلب آموزشی را مورد پسند قرار میده باید خودش از درجه علمی بالایی برخوردار باشه که بتونه بگه مورد پسندم هست یانه! وبرای همین که من دراین سطح نیستم! که فقط بگم پسندیدم! قطعا فقط میتونم بگم تشکر بسیار آموزش مفیدی بود، جناب اسدزاده
  7. متشکرم! دقیقا همینطوره! به نکته ای درستی اشاره کردید. خیلی وقتها به علت وسعت نرم افزار و طبعا هرچه نرم افزار stable بشه در محل نصب مشتری همیشه یکی از چالش های بزرگ این هست که بخواهید تغییری در نرم افزار ایجاد کنید و تضمین کنید که تمام تست های رگرسیون هم بدرستی انجام شده باشه! والبته به همین سادگی هم نیست که بتوان تمام بخش های نرم افزار را تست رگرسیون انجام داد، حتی در صورتی که نرم افزار شما به صورت Test Domain Driven طراحی شده باشد به هرحال کار پر زحمت و پر چالشی هست بنابراین اغلب شرکتهای بزرگ تولید کننده نرم افزار همیشه از تغییرات حساس و بزرگ واهمه دارند و کمتر سمت اش میرن. البته منظور من بیشتر نرم افزارهای کاربردی و حیاتی هستش! بذارید سناریویی که برای خود من اتفاق افتاد را توضیح بدم! در یکی از لینوکس های ردهت که برای مشتری در سالهای قبل نصب کردیم، نیاز داشتیم که نرم افزارهای مشتری را به روز کنیم تا با برخی از سخت افزارهای جدیدتر شرکت بتونن ارتباط داشته باشند، ولی از آنجائیکه ردهت قدیمی بود تغییر در کتابخانه هایی که در پست اول هم اشاره کردم باعث از کار افتادن کرنل میشد بنابراین اومدم یک اسکریپت نوشتم که قبل از اینکه نرم افزار اصلی اجرا بشه مسیر بعضی از کتابخانه هایی که لازم داشتم را با استفاده از همین روش تغییر دادم و تونستم برنامه اصلی را اجرا کنم البته با استفاده از دستورات ld,ldconfig حتما باید لینک ها لازم را برای کتابخانه ها بسازید. بنابراین استفاده از تکنیک preloader library در لینوکس مربوط به یک محیط توسعه خاص نیست، کافی است که در زمان کد نویسی اصول پایه ای برنامه نویسی FURPS را رعایت کنید. پس اگر در کیوت هم که نرم افزار تولید میکنید، باید سعی کنید که به صورت ماژولار برنامه بنویسید و همیشه سعی داشته باشید که از کتابخانه های استاندارد زبان بیشترین بهره را ببرید و تا جایی که امکان داره هسته نرم افزار خودتون را با استانداردها توسعه بدید و از کتابخانه های غیر استاندارد در لایه منطق کمتر استفاده کنید تا وابستگی های کمتری داشته باشید و در زمان های لازم امکان استفاده از چنین تکنیک هایی را داشته باشید. البته به طور قطع به یقین امکان استفاده از چنین تکنیک هایی در لایه های دیگر نرم افزار هم وجود دارده! مثلا تصور کنید که یک کلاس خاص طراحی کرده باشید برای مدیریت ارتباطات بین لایه داده ها و لایه بیزینس نرم افزار تون، فرض کنید تا یک نسخه ای از برنامه شما ملزم به استفاده از پایگاه داده SQL SERVER بودید ولی در نسخ جدید نرم افزارتون نیاز دارید که فقط همین لایه ارتباط با پایگاه داده را توسعه بدید و به یک پایگاه داده دیگه مثل اوراکل متصل کنید و سایر قسمتهای دیگه برنامه به راحتی به کار خودشون ادامه بدهند خوب تحت این شرایط چند تا سناریو داریم... 1- آیا از اول امکانش هست که نیاز سنجی دقیقی داشته باشیم که تمامی این فیچرها را در نرم افزار عملیاتی کرده باشیم؟ خوب قطعا خیر تازه در صورتی هم که میشد کار عاقلانه ای نیست که در زمان حال فیچر ها را به نرم افزار تحمیل کنیم و ... 2- آیا امکانش هست که ما کلاس فوق را با تغییرات جدید به روز کنیم؟ شاید بله جواب این سوال باشه! ولی قطعا به نسبت بزرگی نرم افزار و پیچیدگی های اون درصد موفق بودن این کار خیلی کم میتونه باشه و ازهمه بدتر تست های رگرسیون هم زمان و هم انرژی وهم هزینه ای زیادی خواهد داشت پس بنابراین این کار هم شدنی نیست! تقریبا. 3- آیا امکانش هست که من فقط یک کتابخانه برای کاربر ارسال کنم و الباقی تنظیمات و نرم افزار اصلی بدون خللی به کارشون ادامه بدهند وتازه حلقه تست رگرسیون هم خیلی کوچکتر کنم فقط محدود به خود کتابخانه باشه؟ خوب قطعا با روضه هایی که تاحالا خوندم بله امکانش هست، خیلی راحت در آینده میام یک کتابخانه طراحی میکنم و روش های اتصالات جدید را هم به سیستم اضافه میکنم که هم بتونم به اس کیو ال سرور متصل باشم وهم به اوراکل (البته این پایگاه های داده فقط برای مثال بود) حالا شاید بپرسید که در ویندوز هم امکان چنین چیزی وجود داره! خوب من شخصا چنین چیزی در ویندوز ندیدم که بتوان مسیر کتابخانه های استاندارد زبان را تغییر داد اونهم به این علت که فلسفه وجودی لینوکس و ویندوز خیلی باهم متفاوت هستند و اصولا ویندوز کمتر اجازه میده که مسیر های کتابخانه های سیستمی را تغییر داد چون قطعا با این کار خود ویندوز هم آسیب پذیر میشه همانطور که لینوکس هم می تونه آسیب ببینه! ولی معمولا در ویندوز از DLL ها برای توسعه نرم افزارها استفاده میشود که نحوه ساختن و استفاده از اونها در ویندوز با کتابخانه های Shared , Static کمی متفاوت تر هستش! و اغلب نیازهای نرم افزار را برآورده میکنند. و در سیستم عامل مک او اس هم به علت اینکه unix base هستش قطعا نحوه استفاده از کتابخانه ها خیلی شبیه به لینوکس هستش البته اگر از clang استفاده کنید وبه زبان سی++ کد بزنید! برای اطلاعات بیشترهم این مستند Using Dynamic Libraries in Mac OS را مطالعه کنید.
  8. وقتی برای سیستم عامل لینوکس برنامه می نویسید باید خیلی عملگرا باشید و کدهایی که می نویسید انعطاف پذیر و قابلیت نگهداری بالایی داشته باشند. بنابراین تکنیکی که قصد معرفی اش را دارم نحوه تغییر کتابخانه استاندارد libc در زمان اجرا می باشد. تصور کنید که چنین کدی دارید... #include <stdio.h> int main(void) { puts("Hello, world!"); return 0; } همانطور که مشخص هست از تابع puts استفاده کردم که از کتابخانه های استاندارد c استفاده میکند، بنابراین اگر در آینده قصد داشته باشم که عملکرد تابع puts را تغییر بدم و به سورس اصلی هم دسترسی نداشته باشم، ویا در صورتی که به سورس هم دسترسی داشته باشم ولی امکان تغییر برنامه برای من میسر نباشد! چاره کار این هست که یک کتابخانه بنویسم که در زمان اجرا بدون اینکه برنامه اصلی متوجه شود از تابع puts که در این کتابخانه نوشته ام استفاده کنم. بنابراین کد زیر را می نویسم.... #include <stdio.h> int puts( const char* str ) { return printf("We took control over your C library! \n"); } و با دستورات زیر یک کتابخانه shared object کامپایل میکنم... > gcc -o preload_launcher preload_launcher.c > gcc -c -fPIC prelib.c > gcc -o prelib.so -shared prelib.o اکنون در صورتی که بخوام از رفتار پیش فرض تابع puts که در کتابخانه استاندارد هست استفاده کنم باید به روش زیر عمل کنم... > export LD_PRELOAD= > ./a.out Hello, world! همانطور که دید خروجی تابع با استفاده از کتابخانه استاندارد نمایش داده شد. و اکنون در صورتی که بخوام برنامه از تابع puts من که در کتابخانه prelib.so نوشتم استفاده کنه به روش زیر عمل خواهم کرد... > export LD_PRELOAD=$PWD/prelib.so > ./a.out We took control over your C library! بنابراین همانطور که مشاهده میکنید خروجی تابع از کتابخانه جدید استفاده کرد. در دستور بالا PWD$ به شاخه پیش فرض home/yourusername شما اشاره میکند در حقیقت متغیر سیستمی هست. حالا تصور کنید که یک برنامه safety critical نوشتید و در سازمان مورد نظر هم نصب وراه اندازی شده و دوره های تست راهم پشت سر گذاشته ودر حال کار هست! قطعا درآینده نیاز دارید که عملکرد بخش هایی از این برنامه را تغییر دهید، و البته به راحتی نمی توانید که برنامه را تغییر بدید و مجددا دوره تست زمان اجرا بگیرید به علت های زیادی که در نرم افزارهای حیاتی وجود دارد، بنابراین تغییر کتابخانه های بهترین روش هست(البته حتما باید خود کتابخانه جدید کاملا به صورت کامل مراحل تست را انجام داده باشه) ویا حتی تصور کنید که با یک نسخه قدیمی از لینوکس برنامه خودتون را اجرا کردید، قطعا میدونید که به روز کردن کتابخانه ها در لینوکس های قدیمی کار خیلی مشکلی هست و هر لحظه امکان داره که کرنل لینوکس از کار بیوفته! برای همین به راحتی نمی تونید کتابخانه ها را کپی کنید و به روز کنید علی الخصوص کتابخانه هایی مثل ldlinux.so , libc.so,libstdc++6.so ولی در صورتی که به اسکریپت نویسی و دستورات لینوکس اشراف داشته باشید می توانید با همین دستورات مانند export , ld, ldconfig ,... به راحتی برنامه هایی که با کامپایلرهای به روز تر کامپایل میکنید را در کرنل لینوکس های قدیمی اجرا کنید.
  9. در خیلی از مواقع که با داده های بزرگ سرکار داریم، نوشتن برنامه های تک نخی بسیار کند و پر هزینه می تونه باشه! که البته ما دراین تاپیک قصد بیان مزایا و معایب تک نخی و چند نخی را نخواهیم داشت، بلکه به یکی از روشهای پر کاربرد در زمینه برنامه نویسی چند نخی می خواهیم اشاره کنیم که به divide and conquer (تقسیم و غلبه) شناخته میشود. به مثال زیر دقت کنید... #include <unistd.h> #include <inttypes.h> #include <stdio.h> #include <malloc.h> uint64_t factors( uint64_t num ) { uint64_t result = 0; for (uint64_t i = 1; i <= num; i++ ) if ( num % i == 0 ) result++; return result; } int main( void ) { /* volatile to prevent constant propagation */ volatile uint64_t input = 2000000000; printf( "Factors of %"PRIu64": %"PRIu64"\n", input, factors(input) ); return 0; } همانطور که مشاهده میکنید در صورتی که تکه برنامه فوق را به صورت زیر کامپایل کنیم... > gcc -O3 -std=c99 -o sp fact_sp.c && \time ./sp زمانی که برای اجرای این تابع صرف خواهد شد چیزی بین 40 تا 50 ثانیه خواهد بود. اکنون همین برنامه را به صورت چند نخی به صورت تقسیم و غلبه بازنویسی میکنیم... #include <pthread.h> #include <unistd.h> #include <inttypes.h> #include <stdio.h> #include <malloc.h> #define THREADS 4 struct fact_task { uint64_t num; uint64_t from, to; uint64_t result; }; void* fact_worker( void* arg ) { struct fact_task* const task = arg; task-> result = 0; for( uint64_t i = task-> from; i < task-> to; i++ ) if ( task->num % i == 0 ) task-> result ++; return NULL; } uint64_t factors_mp( uint64_t num, size_t threads_count ) { struct fact_task* tasks = malloc( threads_count * sizeof( *tasks ) ); pthread_t* threads = malloc( threads_count * sizeof( *threads ) ); uint64_t start = 1; size_t step = num / threads_count; for( size_t i = 0; i < threads_count; i++ ) { tasks[i].num = num; tasks[i].from = start; tasks[i].to = start + step; start += step; } tasks[threads_count-1].to = num+1; for ( size_t i = 0; i < threads_count; i++ ) pthread_create( threads + i, NULL, fact_worker, tasks + i ); uint64_t result = 0; for ( size_t i = 0; i < threads_count; i++ ) { pthread_join( threads[i], NULL ); result += tasks[i].result; } free( tasks ); free( threads ); return result; } int main( void ) { uint64_t input = 2000000000; printf( "Factors of %"PRIu64": %"PRIu64"\n", input, factors_mp(input, THREADS ) ); return 0; } بنابراین اگر کد فوق راهم به صورت زیر کامپایل کنید... > gcc -O3 -pthread -o mp -std=c99 dist_fact_mp.c && \time ./mp حداکثر زمانی که برای اجرای این برنامه صرف خواهد شد 6 الی 7 ثانیه می باشد. البته با بیشتر کردن تعداد نخ ها بازدهی بیشتری خواهید داشت ولی دراین تکنیک بهتر است نهایت تا 8 نخ استفاده شود. پس در صورتی که تکنیک های برنامه نویسی چند نخی را رعایت کنید، واقعا معجزات این روش برنامه نویسی را خواهید دید، البته توجه داشته باشید که در پردازشگرهای امروزی نوشتن برنامه های تک نخی ظلم در حق این پردازشگرهاست. تصور کنید که در یک سیستم عامل 64 بیتی با یک پردازشگر مثل i7 بخواهید برنامه تک نخی بنویسید، دقیقا مصداق بارز خریدن مرسدس بنز هست که فقط باهاش 80 کیلومتر سرعت برید خوب قطعا بهینه استفاده نکردید و در آخر هم به مکانیزم این روش تقسیم وغلبه و fork and merge کردن نخ ها باهم که در تصویر زیر آمده است توجه فرمایید... و البته در برنامه چند نخی که دربالا آمده است برای ساده تر کردن برنامه از merge کردن مقادیر در pthread_join استفاده نشده است، از یک مقدار اشاره گر عضو یک ساختار جهت تجمیع مقادیر استفاده شده است. موفق باشید.
  10. فرهاد شیری

    خوب وقتی 0f.% گذاشتید رند میکنه، تعداد اعشاری که میخواهید را مشخص کنید درست میشه 2f.%
  11. با سلام و عرض ادب همانطور که میدانید، استفاده از اشاره گرهای RAW کمی دردسر و مخاطراتی را به همراه داره، بنابراین در استاندارد های جدید زبان سی++ از اشاره گرهای هوشمند استفاده میشود.که هم اشیاء سبک وزنی هستند وهم نکته حائز اهمیت شون این هست که دیگه برنامه نویس نگران بازپس گیری منابع حافظه ای نخواهد بود این اشاره گرها خودشون براساس مکانیزم داخلی شون عملیات بازپس گیری منابع حافظه ای را انجام میدهند. نکته قابل توجه اینکه مکانیزم این اشاره گرها برای بازپس گیری منابع حافظه ای با آن چیزی که در GC زبانهای مدیریت شده وجود دارد، متفاوت هست در اینجا از قوانین scope resolution منابع که توسط کامپایلر تعریف میشود استفاده میشود! ولی معمولا Garbage Collector در زبانهای مدیریت شده بر اساس شمارش رفرنس منابع اقدام به بازپس گیری میکنند. که یکی از پرکاربردترین این اشاره گرهای هوشمند std::shared_ptr هست که به صورت یک template class استاندارد زبان تعریف شده. بنابراین زمانی که نیاز به یک اشاره گر هوشمند داشتید که حتما باید منبع تعریف شده خودش را به اشتراک بذاره! می توانید از این اشاره گر استفاده کنید. به مثال زیر دقت کنید... class Resource{ public: Resource() { std::cout << "Resource acquired\n"; } ~Resource() { std::cout << "Resource destroyed\n"; } }; int main(){ // allocate a Resource object and have it owned by std::shared_ptr Resource *res = new Resource; std::shared_ptr<Resource> ptr1(res); { std::shared_ptr<Resource> ptr2(ptr1); // use copy initialization to make another std::shared_ptr pointing to the same thing std::cout << "Killing one shared pointer\n"; } // ptr2 goes out of scope here, but nothing happens std::cout << "Killing another shared pointer\n"; return 0; } // ptr1 goes out of scope here, and the allocated Resource is destroyed همانطور که از مثال مشخص هست، در صورتی که در دو بلاک مختلف بخواهید از یک منبع مشترک که یک اشاره گر می باشد استفاده کنید، به راحتی با استفاده از کلاس std::shared_ptr امکانپذیر می باشد. ولی در برخی از مواقع شما نیاز دارید که یک عضو داخلی در کلاس Resource تعریف کنید از نوع shared_ptr که به منبعی از نوع همین کلاس resource اشاره داشته باشد. مانند مثال زیر... class Resource{ public: std::shared_ptr<Resource> m_ptr; // initially created empty Resource() { std::cout << "Resource acquired\n"; } ~Resource() { std::cout << "Resource destroyed\n"; } }; بنابراین در صورتی که بخواهید این عضو کلاس Resource را مقدار دهی کنید می توانید از روش زیر استفاده نمایید. int main(){ auto ptr1 = std::make_shared<Resource>(); ptr1->m_ptr = ptr1; // m_ptr is now sharing the Resource that contains it return 0; } ولی مشکلی که در اینجا به وجود می آید، این هست که بعد از اینکه شی ptr1 از بین رفت، شی عضو کلاس m_ptr از بین نخواهد رفت به این علت که یک shared_ptr هست و نیز یک instance reference به منبع Resource وجود دارد، بنابراین بعد اینکه شی ptr1 از بین رفت دیگر دسترسی به عضو کلاس m_ptr هم نخواهیم داشت، که در اینجا مشکل Cyclical Reference پیش خواهد آمد. اکنون برای رفع این مشکل کمیته استاندارد سازی زبان یک شی سبک وزن دیگه به نام std::weak_ptr را تعریف کرده که با همان مکانیزم weak reference ها در شی گرائی جهت جلوگیری از tight coupling ها در ایجاد وابستگی ها، تعریف شده. بنابراین در صورتی که میخواهید از shared_ptr به عنوان یک عضو کلاس استفاده کنید و منبع مشترک تون در یک حوزه دیگه هست کافی که به این روش عمل کنید... #include <iostream> #include <memory> // for std::shared_ptr class Resource{ public: std::weak_ptr<Resource> m_ptr; // initially created empty Resource() { std::cout << "Resource acquired\n"; } ~Resource() { std::cout << "Resource destroyed\n"; } }; int main() { auto ptr1 = std::make_shared<Resource>(); ptr1->m_ptr = ptr1; // m_ptr is now sharing the Resource that contains it return 0; } پس بنابراین برای حل مسئله Cyclical Reference که در بالا توضیح داده شد weak_ptr طراحی شده است، weak_ptr فقط ناظر است - می تونه همون منبع را به عنوان یک shared_ptr (یا یک weak_ptr دیگر) مشاهده کنه و به آن دسترسی پیدا کنه ، اما مالک منبع محسوب نمیشه، درآخر هم خاطر نشان کنم که، هنگامی که یک اشاره گر مشترک shared_ptr از محدوده خارج میشه، این مسئله را در نظر می گیره که آیا سایر shared_ptr در حال مالکیت شی هستند، یعنی instance reference دیگه ای به منبع وجود داره یا خیر. که در این بازبینی منبع اشاره گر weak_ptr حساب نمیشود! بنابراین به همین علت بعد از خارج شدن از حوزه دید عضو weak_ptr کلاس این اشاره گر از بین میرود ومنابع به حافظه بر میگردد. امیدوارم مفید فایده قرار بگیرد.
  12. فرهاد شیری

    تصور کن! سیستم نرم افزاری حمل و نقل ریلی را توسعه دادید! در این سیستم به صورت گرافیکی تمام خطوط ریلی ایستگاه و تمام علائم مربوطه با جزئیات مشخص شده و همچنین نحوه حرکت قطارها در این سیستم به صورت لحظه ای نمایش داده میشه! بنابراین عمکلرد محیط گرافیکی کاربری نرم افزار خیلی حائز اهمیت هست که تمام المانها به درستی وظیفه خودشون را انجام بدهند، بنابراین ابزارهای مانتیورینگ عملکرد محیط کاربری برنامه ها خیلی مهم و کاربردی خواهد بود.
  13. فرهاد شیری

    با سلام و عرض خوش آمد گویی به انجمن خیلی خوبه که شرکت شما در چنین سطحی از سی++ می خواهد استفاده کنه! ولی بهتر بود، شما که با چنین جزئیاتی خواسته های خودتون را از برنامه نویس خواستید، در مورد مزایای کاری شرکت و همچنین نام و رسم شرکت هم توضیح ارائه میکردید. به هرحال این توصیفات یک برنامه نویس فول استک که بیشترین تجربه را در سمت سی و سی++ داشته باشه هست، که با کمتر از ماهی 7 میلیون تومان حداقل حقوق البته به غیر از مزایای قانونی می باشد. امیدوارم موفق باشید.
  14. فرهاد شیری

    فارغ از صحت عملکردش در شرایط مختلف، ایده فوق العاده عالی و کارآمدی هست علی الخصوص درسیستم های Safety Critical که عملکرد جزء به جزء نرم افزار بسیار مهم و حیاتی هست!
  15. در خیلی از مواقع نیاز داریم که با استفاده از آرگومانهای ورودی تابع Main برنامه خودمون ، در زمان اجرا شدن بر اساس این آرگومانهای ورودی برنامه رفتارهای متفاوتی را برای ما اجرا کنه! در چنین شرایطی حتما به یک آرگومان پارسر نیاز خواهیم داشت که بتوانیم این آرگومانها که در آرایه وروی تابع Main ذخیره شده اند، را ازهم تفکیک کنیم تا بتوانیم از این آرگومانها استفاده ببریم. در تابع Main (تابع شروع کننده اصلی برنامه) در سی++ به صورت پیش فرض دو پارامتر ورودی وجود دارد... 1- پارامتر argc : که از نوع int می باشد و تعداد پارامترهای وارد شده به برنامه را در خود جای داده است.(توجه داشته باشید نام فایل اجرایی خود برنامه هم به عنوان یک مقدار محاسبه می شود.!) 2- پارامتر argv: که معمولا از نوع *char می باشد، ولی میتوان از نوع های دیگر هم استفاده کرد، در این آرایه مقدار آرگومانهای ورودی برنامه شما ذخیره شده است.(توجه داشته باشید نام فایل اجرایی خود برنامه هم در اولین عضو این آرایه قرار دارد.!) بنابراین با توضیحات فوق با استفاده از پارسر زیر می توانید از این آرگومانها استفاده نمایید. جهت استفاده هم به طریق زیر عمل کنید... ./testArgs 1 -s 2 #include "stdafx.h" #include <iostream> #include <cstring> #include <cstdlib> #include <typeinfo> #ifdef WIN32 //if compile in windows with VisualC++ #define COMPILE_TYPENAME_PTR_CHAR "char *" #define COMPILE_TYPENAME_PTR_INT "int *" #elif // if compile in linux with GCC #define COMPILE_TYPENAME_PTR_CHAR "Pc" #define COMPILE_TYPENAME_PTR_INT "Pi" #endif #define BYTE char #define PBYTE char* #define DWORD int using std::cout; using std::endl; using std::strcmp; template<typename T> class CParser { public: using ARGS_TYPE = T; CParser() {}; ~CParser() {} //CParser(ARGS_TYPE& args) : m_args(args) {} //CParser(ARGS_TYPE&& args) : m_args(std::forward<ARGS_TYPE&&>(args)) {} void setArgs(ARGS_TYPE& args) { m_args = std::forward<ARGS_TYPE&>(args); } auto getArgByIndex(const DWORD& index, ARGS_TYPE args = m_args) -> decltype(*(args + index)) { decltype(*(args + index)) element{ *(args + index) };//define reference of array element. return element; } void invokeC(const DWORD& index) { auto& arg = getArgByIndex(index); if (strcmp(typeid(arg).name(), COMPILE_TYPENAME_PTR_CHAR) == 0) { if (strcmp(arg, "-s") == 0) { cout << "run app in mode x (" << index << "): " << arg << endl; } if (std::strcmp(arg, "1") == 0) { int evalArgNum = std::atoi(arg); cout << "run app in mode y (" << index << "): " << evalArgNum << endl; } if (std::strcmp(arg, "2") == 0) { int evalArgNum = std::atoi(arg); cout << "run app in mode z (" << index << "): " << evalArgNum << endl; } } } void invokeI(const DWORD& index) { auto& arg = getArgByIndex(index); if (std::strcmp(typeid(arg).name(), COMPILE_TYPENAME_PTR_INT) == 0) { if (*arg == 49) { //ascii 1 int evalArgNum = std::atoi(reinterpret_cast<const PBYTE>(arg)); cout << "run app in mode y (" << index << "): " << evalArgNum << endl; } if (*arg == 50) { //ascii 50 int evalArgNum = *arg; cout << "run app in mode z (" << index << "): " << evalArgNum << endl; } } } protected: private: /*this membar is define static, because when the type is template arg , must can be set the default value when declare in method signture. */ static ARGS_TYPE m_args; }; template<typename ARGS_TYPE> ARGS_TYPE CParser<ARGS_TYPE>::m_args= nullptr ; using PARSER_CHAR = CParser<BYTE**>; using PARSER_INT = CParser<DWORD**>; int main(DWORD argc, BYTE** argv) { PARSER_CHAR parser; parser.setArgs(argv); for (int i = 0; i < argc; i++) { parser.invokeC(i); } return 0; } در صورتی که ابهامی در کد وجود داشت لطفا در همین تاپیک اعلام نمایید.
×
×
  • جدید...