C++. Список инициализации членов в конструкторе. Альтернативный синтаксис

Существует альтернативный синтаксис инициализации членов классов используя так называемый список инициализации, в котором через запятую указываются имена и начальные значения. Например, конструктор по умолчанию можно переписать следующим образом:
 
// конструктор по умолчанию класса Test с использованием
// списка инициализации членов int member1, double member2, int member3
 
inline Test:: Test() : member1(0), member2( 0.0 ), member3( 0 ) {};
 
Такой список допустим только в определении, но не в объявлении конструктора. Он помещается между списком параметров и телом конструктора и отделяется двоеточием. Вот как выглядит конструктор с двумя параметрами при частичном использовании списка инициализации членов:
 
inline Test:: Test(int m1, double m2, int m3) : member1(m1), member2(m2), member3(m3) {};
 
Важно помнить о соблюдении порядка объявления членов, которые мы хотим инициализировать через список в конструкторе: порядок объявления должен быть равнозначен порядку инициализации, например:
 
class Test {
    // Порядок объявления членов класса
    int b;
    int a;
public:
    Test(int x) : a(x), b(a) {}; // Нарушен порядок
};
 
При этом компилятор может не проругаться, однако переменные будут содержать неоднозначные значения (мягко говоря). На последок небольшой рабочий пример.
 
#include <iostream>
using namespace std;
 
class Test {
   int a;
   int b;
public:   Test() : a(16), b(61) {};
   //Test(int x) : a(x), b(a) {}; // Нарушен порядок
   void show() {cout << "a = " << a << " b = " << b << endl;};
};
 
int main() {
   Test t;
   //Test t(5); // Для вызова конструктора с нарушенным поядком объявления и инициализации
   t.show();
}

 

C++. Указатели на функции, массивы указателей на функции

Указатели сами по себе очень полезны в работе, но в то же время коваврны, потому что с ними нужно быть очень аккуратным, иначе не избежать утечки памяти. Ниже рассмотрено как можно применять указатели к функциям. Предположим, что у нас есть декларированные функции:

int f1(int); int f2(int); int f3(int);

Тогда можно объявить указатель:

int (*p) (int);

Скобки здесь обязательны так как тогда компилятор воспримет это выражение так:

int* p(int);

Ну а это объявление функции с возвращаемым типом типа указатель. Продолжим.
Теперь, ему можно присвоить ссылку на одну из функций:
 
p = f1;

Если прописать так,

p = f1();

то это приведет к попытке вызова функции и тем самым вызовет ошибку компиляции.
После этого можно вызывать функцию следующим образом:

(*p) (16); // p (16); Так тоже работает

Стоит отметить, что при объявлении указателей на функцию, необходимо соблюдать согласованность типов между указателями и функциями, например так будет некорректно:
 
int (*p) (); // Это равнозначно вырыжению int (*p) (void);

А это уже несогласованность!
Есть еще один нюанс. Предположим, что функция декларирована с аргументами по-умолчанию
 
int f4(int n = 0);

И казалось можно написать так

int (*p) (int);
p = f4;
p ();

И я получаю ошибку компиляции

error C2198: 'int (__cdecl *)(int)' : too few arguments for call

Поэтому нужно будет передать конкретное значение

p (16);

А значит вызывать функцию с расчетом на использование параметров по-умолчанию - не получится. Но все же указатели могут работать и с перегруженными методами. Как общий случай, рассмотрим следующее. Определим шаблон функции из типов данных, определяемых пользователем:

template <class T>
T getme(T a, T b) {

       return (a>b?a:b);
}


Тогда при объявлении указателя будет опрелен тип функции getme()
 
char (*tpl) (char, char);
tpl = getme;
cout << "from template function getme() " << tpl ('a', 'b') << endl;


Для удобства работы с указателями можно определить синоним указателей на функцию, например

typedef int (*F) (int); // Сие эквивалентно выражению int (*) int;

Таким образом мы получили синоним указателей - F. Далее с ним работаем вот так

F p; // Сие эквивалентно выражению int (*p) int;

Так мы объявили указатель на функции, ну а дальше по аналогии

p = f1;
p (16);

Теперь рассмотрим массив указателей на функции. Здесь ничего сложного, итак, объявим сие массив:

int (*pm[]) (int) = {f1, f2, f3};

Данный массив статический, поэтому при его создании нужно определять его размер. Для вызова соответствующей функции нужно обратиться к соответствующему элементу массива

(*pm[0]) (16);

На этом закончу. Если что, то исходник можно скачать здесь.

C++. struct like a class

Есть простые типы данных, есть и сложные, определяемые пользователем - пользовательский тип данных. В чистом C - это struct id {}, union id {} и enum id {}. В C++ - class id {}, причем понятие класса здесь настолько широкое, что оно включает в себя и struct id {}, и union id {}, и enum id {}. Поэтому структура может иметь свои методы, идентификаторы доступа (public:, private:, protected:), в общем мы имеем дело с объектной ориентацией. Например, такое в C не прокатит:

#include <iostream>
using namespace std;

struct first {
private:
   int a;
};

struct test {
   int n;
} tpl;

struct sec: first {
   int s;
   sec() {
     cout << "***" << endl;
     a = 16;
 }
 void f(int param = 0) {
    cout << "in sec struct function" << endl;
    p.s = param;
    cout << "s = " << p.s << endl;
    (p.s)++;
    cout << "s++ = " << p.s << endl;
 };
 void show() {
    cout << "a = " << a << endl;
 }
} p;

void testing() {
   cout << "in testing function" << endl;
}

test func(int n = 0) {
   tpl.n = n;
   return tpl;
}

int main() {
 testing();
 func();
 cout << "func(0). tpl.n = " << tpl.n << endl;
 func(16);
 cout << "func(16). tpl.n = " << tpl.n << endl;

 p.f(16);
 cout << "Outline. p->s = " << p.s << endl;
 p.show();

 return 0;
}


Стоит только отметить, по-умолчанию ( если явно не указывать ) тип доступа в struct id {}; - public:, а в class id {}; - private:



C++. Convert from 'std::string' to 'const wchar_t *'. Нюанс

При автоматической генерации выходных файлов, мало ли какие на сие есть потребности, нужно предусмотреть автоматическое именование. Для этого удобней всего работать со стандартным классом string. Есть один нюанс. Если при открытии файлового потока (создание файла) в качестве параметра передать переменную строкового типа string, можно столкнуться с тем самым нюансом. Компилятор выдаст ошибку - cannot convert parameter 1 from 'std::string' to 'const wchar_t *'. М-да, видимо для этого случая перегрузка метода не предусмотрена. Что делать? Для этого предусмотрен метод класса string - c_str().

#include <iostream>
#include <string>
#include <fstream>

using namespace std;

void main(void)
{
    char temp[16];
    string nameFile = "test.txt";
    ofstream out;
 
    // Место нюанса. Без соответствующего метода компилятор ловит ошибку несоответствия
    // out.open(nameFile, ios::out);
    // Если так, то компиляция проходит успешно
    out.open(nameFile.c_str(), ios::out);
    // При необходимости записи строки типа string нужно также использовать сие метод
    out.write("Hello, world", 12);
    out.close();

    // waiting your action...
    cin >> temp;

}