کدنگار

وبلاگ شخصی-آموزشی علی رشیدی

کدنگار

وبلاگ شخصی-آموزشی علی رشیدی

طبقه بندی موضوعی
پیوندهای روزانه
پیوندها

درود!

۱۰
خرداد

به وبلاگ شخصی من خوش آمدید!

یادداشت ها و نوشته های شخصی، آموزش ها و ... خودم رو اینجا میذارم. پیدا کردنشون هم آسونه، دسته بندی موضوعی رو نگاه کنید!

کپی با ذکر منبع بلامانع است.

این وبلاگ از جای دیگری کپی نمیکند.

اوقات خوشی را برایتان آرزومندم.

.:*علی رشیدی*:.

  • علی رشیدی

در این قسمت خط لوله، فیلترها و خواندن و نوشتن فایل ها را بررسی میکنیم.

دانلود PDF

  • علی رشیدی

قسمت دوم: استفاده از اشیا، سازنده ها

استفاده از اشیا

پس از ساخت یک کلاس، باید آن را نمونه سازی کنیم. یعنی یک نمونه از این شی را بسازیم و استفاده کنیم. به نمونه ساخته شده یک شی از نوع یک کلاس (An object of type class) میگوییم. مثلا اگر کلاسی به نام Student داشته باشیم و یک نمونه از آن به اسم stu1 تعریف کنیم، stu1 یک شی از نوع Student خواهد بود.

Student stu1;

از اینجا به بعد یک کلاس نیز یک نوع داده است (مانند انواع اولیه مثل int و struct ها) و میتوان از آن یک نمونه یا ارایه تعریف کرد یا اشاره گری به آن کلاس داشت (همانطور که قبلا اشاره گر به انواع اولیه مانند char داشتیم)

Student stu;
Student *stuPtr;
Student students[12];
stuPtr = &stu;

سازنده ها

هر کلاس میتواند یک متد خاص به نام سازنده (constructor) داشته باشد. این متد از این لحاظ خاص است که هنگام ساخت یک شی صدا زده میشود و میتوانید کارهایی که نیاز است هنگام ساخت یک شی انجام دهید (مانند مقدار دهی اولیه متغیر ها) را توسط آن انجام دهید. این متد هم نام با نام کلاس است و هیچ نوع برگشتی (حتی void) ندارد. مثال:

class TestClass
{
public:
TestClass()
{
val = 5;
}
int getVal()
{
return val;
}
void setVal(int p)
{
val = p;
}
private:
int val;
};

int main()
{
TestClass t;
cout << t.getVal();
return 0;
}

خروجی این برنامه 5 است.

سازنده ها میتوانند پارامتر هم داشته باشند، در این صورت هنگام ساخت یک نمونه، باید آن پارامتر را هم ارسال کنیم:

public:
TestClass(string n)
{
val = 5;
name = n;
}
private:
string name;

در قسمت بعد روش جداسازی پیاده سازی و تعریف، و overload کردن توابع را بررسی میکنیم.

  • علی رشیدی

قسمت اول: مفاهیم اولیه

شی گرایی یعنی اینکه هر مفهوم و مدلی را در برنامه نویسی بتوانیم با استفاده از یک شی پیاده سازی کنیم. مثلا یک کتابخانه را در نظر بگیرید: این کتابخانه دارای خصوصیاتی مانند نام، ظرفیت کتاب و لیستی ازکتاب هاست. از سوی دیگر این کتابخانه دارای رفتار هایی مانند حذف، اضافه و جستجو است. این دو ویژگی تقریبا در هر شیئی یافت میشوند. یک سنگ جرم دارد و میچرخد، یک ماشین اسم دارد و سوحت مصرف میکند و ... . پیاده سازی مدل ها، ساختمان داده ها و تقریبا هر چیز با این طرز فکر، برنامه نویسی را ساده، قابل فهم و لذت بخش میکند.
حالا که خصوصیات کلی اشیا را میدانیم بیایید با یک مثال به بررسی پیاده سازی چنین چیزی در ++C بپردازیم.

در این مطلب:
  • معرفی ساختار یک شی
  • پیاده سازی یک شی دانش آموز (کد در انتهای مطلب فابل دانلود است)
  • آزمایشگاه
  • علی رشیدی

حدود یک ماه است که روی پروژه ای که بند بند وجودش به پردازش جیسون و محلی سازی است کار میکنم، تا حالا تجربه های کوچکی را با شما به اشتراک گذاشتم و حالا میخواهم در این مطلب، ارسال درخواست، ذخیره نتیجه، پردازش و محلی سازی جیسون را به وسیله اشیا توضیح بدهم.


ارسال درخواست و دریافت نتیجه

برای ارسال درخواست، تست شده ترین و ساده ترین روش به شرح زیر است:


  • ایجاد یک شی QNetworkAccessManager، QNetworkRequest و QNetworkReply
  • QNetworkAccessManager برای ارسال درخواست
  • QNetworkRequest درخواستی است که به شی قبل میدهیم.
  • QNetworkReply هم پاسخ یک درخواست را نگهداری میکند.
  • تنظیم مشخصات request و ارسال، برابر قرار دادن reply با شی بازگشت داده شده توسط request.
  • یک QEventLoop را اجرا میکنیم، این حلقه تا زمانی که اسلات quit آن صدا زده نشود برنامه را متوقف میکند.
  • سیگنال finished از request را به این اسلات وصل میکنیم، تا هنگامی که درخواست کامل شد (پاسخ کامل دریافت شد) از حلقه خارج شویم.
در نهایت ما یک شی از نوع QNetworkReply داریم که میتوانیم نتیجه درخواست را از آن بخوانیم.

//Files to include
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QEventLoop>
#include <QObject>
#include <QUrl>

//Example code
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
QNetworkRequest request;
QNetworkReply *reply;
QEventLoop loop;

connect(manager, SIGNAL(finished(QNetworkReply*)), &loop, SLOT(quit()));

request.setUrl(QUrl("Where we receive JSON from"));
reply = manager->get(request);
loop.exec();

پردازش جیسون

ایجاد سند

حالا که نتیجه درخواست را دارید (که قطعا یک جیسون معتبر باید باشد) میتوانید آنرا پردازش کنید. اولین قدم، ایجاد یک شی از نوع QJsonDocument است. برای اینکار از متد استاتیک fromJson این شی استفاده میکنیم.


نکته: متد استاتیک، متدی است که بدون اینکه یک شی ایجاد شده باشد قابل دسترسی است.


روش ایجاد یک سند با استفاده از نتیجه درخواست:

QJsonDocument doc = QJsonDocument::fromJson(reply->readAll());
if (doc.isNull())
qDebug() << "Unable to load JSON or invalid JSON document";

متد isNull بررسی میکند که سند ایجاد شده یا خیر. اگر ایجاد نشده باشد، ممکن است اصلا اطلاعات دریافت نشده باشد یا اینکه اطلاعات دریافتی دارای مشکل باشد.


کار با اشیا

من فرض را بر این میگیرم که ساختار جیسون را میدانید. کیوت برای اشیا جیسون کلاسی دارد به اسم QJsonObject. این کلاس میتواند یک شیئ را نگهداری کند.

اگر میخواهید راحت تر جیسون را پردازش کنید، بهتر است به صورت شی یا آرایه به آن دسترسی پیدا کنید. اگر میدانید ریشه سند شما یک شی است، به این صورت عمل کنید:

QJsonObject obj = doc.object();

توجه کنید که اگر ریشه سند آرایه باشد شی ایجاد شده تهی است.

حالا میخواهیم به یک کلید از سند دسترسی پیدا کنیم، اینکار با استفاده از براکت ها انجام میشود. مثلا اگر شی ما یک کلید به اسم name دارد به صورت زیر قابل دسترسی است:

obj["name"]

اما این داده نمیتواند به تنهایی کاری برای ما انجام دهد چرا که از نوع QJsonValue است. برای تبدیل آن به شی مناسب از متد های توضیح داده شده در مستندات کیوت، مربوط به QJsonValue استفاده کنید. مثلا ما میدانیم که نام یک رشته است، پس اینگونه آنرا مورد استفاده قرار میدهیم:

qDebug() << obj["name"].toString();
//Outputs "Ali"

کار با آرایه ها

برای دسترسی به هر عضو آرایه از متد at استفاده میکنیم. این متد هم یک QJsonValue برمیگرداند که کار تبدیل را راحت میکند.

مثلا اینجا یک عضو آرایه را به عنوان شی استفاده میکنیم:

/*Our JSON is:
[{"name":"Ali","age":18},{"name":"Sina","age":21}]
and is stored in arr
*/

QJsonObject aliObject = arr.at(0).toObject();
qDebug() << aliObject["name"];
//Outputs "Ali"

مثال بزرگتر

در مطلب بعدی یک مثال خواهم گذاشت که در آن خواندن جیسون از فایل، محلی سازی، نوشتن در فایل و ... انجام شده و برای مطالعه عالی میباشد.

  • علی رشیدی

تجربه ۳ رو یادتونه؟ که برای یه آرایه نامشخص انجام دادیم. یگ مشکل اساسی اون روش، این بود که برای یه آرایه دو بعدی یا بهتر بگم در کار من، آرایه ی آرایه، این روش بسیار پیچیده و بیشتر شبیه به ماست مالی میشد. و اینجا بود که به یاد دوست قدیمیمون وکتور افتادم. وکتور خیلی از مشکلات از قبیل سایز و ... رو نداره و خلاصه خیلی کارو راحت میکنه. میشه ریسایزش کرد و خلاصه خوراک کار من بود.


اما کاری که من میخواستم بکنم این بود: یک وکتورِ وکتور رو از روی یه آرایه ی آرایه که با جیسون دیکُد شده بسازم. روش این شد که یک وکتور وکتور به روش زیر بسازم، بعد بُعد اول رو بر اساس سایز بعد اول آرایه جیسون تغییر اندازه(ریسایز) کنم، و بعد برای هر بعد دوم، با استفاده از جیسون باز هم ریسایز کنم. در واقع یه چیزی شبیه به این:


    QVector<QVector<A>> myVect;
myVect.resize(2);
int i, j;

for (i = 0; i < 2; i++)
{
myVect[i].resize(4);
for (j = 0; j < 4; j++)
{
myVect[i][j] = A(i*j);
}
}

این روش، البته، یه جورایی فقط به درد من میخوره که سایز ها موقع تعریف وکتور برام مشخص نیست. اگر سایز وکتورتون مشخصه میتونید وکتور رو اینطوری تعریف کنید:


QVector<QVector<A>> myVect(3, QVector<A>(5));

در اینجا وکتورمون مثل یه آرایه هست که اینطوری تعریف شده باشه:


int arr[3][5];

تعریف کردن و ریسایز و ... در وکتور استاندارد هم کاملا مشابه همینه و اگه از اون استفاده میکنید کافیه QVector رو با vector جایگزین کنید.

  • علی رشیدی

در قسمتی دیگر از پروژه، این بار قرار بود که اطلاعات یک آرایه پویا (که از نوع یک شی بود) را بخوانم و پردازش کنم... خب، مشکل این بود که سایز آرایه نامعلوم بود و هنگام پردازش با خطا مواجه می شد. در واقع کلاسی که من داشتم:


class Test
{
public:
void print()
{
...
}

void setObject(MyObject *obj)
{
object = obj;
}

private:
MyObject *object;
};

خب، ست کردن آرایه که کاری نداشت اما مشکل در تابع print بود، چگونه بدون دانستن سایز آنرا چاپ کنیم؟ اول سرچ کردم و روش مشخصی پیدا نکردم، پس تصمیم گرفتم که ببینم اصلا اگر بخواهیم عضوی خارج از محدوده را بخوانیم چه میشود؟ در تابع پرینت عبارت


cout << object[4].getText();

را وارد کردم و این شی رو با یک آرایه ۴ عضوی ست کردم، و هنگام اجرا متوجه شدم که


terminate called after throwing an instance of 'std::bad_alloc'
what(): std::bad_alloc

این یک استثنا بود (Exception) و برای کنترل آن راحت می شد از try & catch استفاده کرد. پس پرینت را به این صورت نوشتم:


void print()
{
int i;
bool end;
try
{
i = 0;
while(!end)
{
cout << object[i].getText();
i++;
}
}

catch (const std::bad_alloc&)
{
end = true;
}
}

خیلی ساده، به محض اینکه به اولین عضو خارج از دامنه برسیم، استثنا رخ میدهد و خواندن ارایه متوقف میشود.

  • علی رشیدی
یه قسمت دیگه از پروژه، باید اطلاعات جیسون رو میگرفتم و در یک شی ذخیره میکردم. اما به یه مورد عجیب برخوردم: آرایه ارایه. خب اینا تقریبا همون آرایه دو بعدی خودمون هستن تو جیسون، ولی کاری که میخواستم بکنم این بود که یک شی به صورت ارایه دو بعدی رو با شی معادلش در جیسون Init کنم. و در نهایت پیاده سازیش شد اینی که میبینید. یک نمونه سادشو برای خودم انجام دادم با انواع استاندارد ++C تا بعدا توی پروژه اصلی پیاده سازی کنم.

  • علی رشیدی

پروژه جدیدم یک کتابخانه بود و من با وجود اینکه در مورد کتابخانه ها و ساخت آنها با ++g اطلاع داشتم، نمیدانستم که چگونه یک کتابخانه با کیوت بسازم. برای اینکه بفهمم خوب یاد گرفتم یه پروژه ساده انجام دادم که برای شما هم گذاشتمش D:

دانلود

این پروژه از یک کتابخانه و یک برنامه که از اون استفاده میکنه تشکیل شده. اینقد سادست که نیازی به توضیح دادن در موردش هم نمیبینم. البته توجه کنید که کتابخونه به صورت استاتیک ساخته میشه، اگه میخواهید که shared باشه خط 

CONFIG += static

رو حذف کنید تا کانفیگ پیش فرض یعنی shared در نظر گرفته شه.

  • علی رشیدی
درود مجدد!

بعد از مدت ها برگشتم (گرچه دورتر از موعد)، و میخوام که آموزش لینوکس رو همونطور که وعده دادم ادامه بدم.

و همچنین،

همه ی ما برنامه نویسا در طی فعالیتمون،موقع انجام یه پروژه، دیدن یه سوال و ...، ممکنه به مشکل بر بخوریم یا چالشی برامون پیش بیاد، و یادگیری و پیشرفت از همینجا شروع میشه. میخوام هر موقع از این چالش ها برام پیش اومد، یا گهگاهی کدی نوشتم تو وبلاگم با شما به اشتراک بذارم. اگر شما هم چنین کاری میکنید کامنت بذارید تا من هم دنبالتون کنم و از تجربه های همدیگه استفاده کنیم. میدونم که خیلیاتون اینکارو میکنین. D:
  • علی رشیدی

با درود.

در این قسمت با کتابخانه های اشتراکی یا دینامیکی (shared library) آشنا میشوید و یاد میگیرید که چگونه یک پروژه که فقط شامل یک کتابخانه است را با CMake بسازید.

  • علی رشیدی