БлогNot. Можно ли в C++... или каверзные вопросы по языку

Можно ли в C++... или каверзные вопросы по языку

сейчас в списке: 36 вопросов Проверьте себя, хорошо ли Вы понимаете этот замечательный язык.

Ответы даны сразу же по ссылкам оглавления, но лучше сначала подумать.

Вероятно, список будет пополняться и послужит хорошим дополнением к "Типичным ошибкам начинающего разработчика на C++".

Можно из этого сделать и какой-нибудь тест - на сколько вопросов вы смогли ответить или хотя бы рассуждали в правильном направлении?

Все коды проверялись в консоли Visual Studio 2015 или близких версий.

Оглавление

Вычисления, арифметика

Можно ли в C++ сравнить значения 2 переменных без использования оператора сравнения (==)?

Можно ли в C++ поменять местами значения 2 переменных без использования третьей переменной или арифметических операций?

Можно ли в C++ сложить 2 числа, не используя операцию сложения?

Можно ли в C++ умножать или делить целое значение на степени двойки, не используя операций "*" или "/"?

Можно ли в C++ найти максимум или минимум из двух целочисленных значений, не используя условий или циклов?

Как узнать максимальное значение для беззнакового целого типа?

Можно ли в C++ выполнить обе ветви условного оператора "если-то-иначе"?

Можно ли в C++ найти знак числа без использования любого (бинарного или тернарного) условного оператора?

Можно ли в C++ напечатать натуральный ряд чисел, не используя цикла или рекурсии?

Можно ли в C++ напечатать ряд натуральных чисел 1,2,...,N, не используя ни одной точки с запятой?

Можно ли в C++ найти сумму всех цифр числа одним оператором?

Можно ли в C++ прибавить к целому числу единицу, не используя арифметических операций сложения, вычитания или инкремента и декремента?

Можно ли в C++ заполнить двумерную матрицу размерности n x m последовательно идущими числами 1,2,3,..., применяя только один цикл?

Функции, namespace

Можно ли в C++ вызвать необъявленную функцию?

Можно ли в C++ одновременно использовать одноимённые локальную и глобальную переменные?

Можно ли в C++ вызвать функцию слева от знака "="?

Можно ли в C++ из пустой функции main что-нибудь вывести в консоль?

Классы

Можно ли в C++ получить доступ к приватным свойствам класса без использования методов-членов или функций-друзей класса?

Можно ли в C++ сделать класс, объекты которого можно будет создавать только динамически?

Можно ли в C++ сделать класс, объекты которого наоборот можно будет создавать только в стеке, но не динамически в куче?

У класса есть конструктор с одним целочисленным аргументом. Покажите три синтаксически различных способа создать экземпляр класса с помощью этого конструктора

Как запретить автоматическое преобразование типов в конструкторе (не создавать объекты способом 3 из предыдущего вопроса)?

Всегда ли при выполнении операции вида a = b, где a и b - объекты одного класса, вызывается конструктор копирования?

Почему переопределённый оператор присваивания возвращает ссылку на объект?

Как определить для объектов класса сложение с числом в виде Obj+i и i+Obj?

Как получить доступ к элементам, определенным дочерним классом через указатель на базовый класс?

Как классу "выдать себя" за своего предка, то есть, использовать его виртуальный метод?

Как "заставить" класс-потомок определить виртуальный метод?

Как заставить шаблон функции с двумя аргументами обрабатывать по-другому один скалярный тип данных, чем другие сочетания типов?

Как инициализировать члены класса в конструкторе, не обращаясь к ним из тела функции-конструктора?

Как разрешить инициализацию нескольких членов класса в виде X x{ 1, 2 } ?

Когда уместно писать код в файле .h, а в .cpp не стоит?

Разное

Можно ли в C++ проверить порядок байтов (big-endian/little-endian) компьютера?

Можно ли в C++ писать URL-адреса прямо в тексте программы?

Можно ли в C++ из пользовательской программы завершить работу системы?

Почему здесь выведется 2 единицы?

Ответы на вопросы
Можно ли в C++ вызвать необъявленную функцию?

Только если скомпилировать файл типа .c, но не .cpp, например, делать так было можно в "чистом" C:

#include <cstdio>

void f(); /* функция описана без аргументов! */

int main() {
 f(2); /* компилируется в файле .c, но не .cpp */
 getchar();
 return 0;
}

void f(int x) { /* реализация функции имеет аргумент */
 printf ("%d", x);
}
Можно ли в C++ одновременно использовать одноимённые локальную и глобальную переменные?

Да. Оператор области видимости ::

#include <iostream>
using namespace std;

int x = 5;  //глобальная

int main() {
 int x = 10; //локальная
 cout << "global x = " << ::x << endl;
 cout << "local x = " << x;
 cin.get(); return 0;
}
Можно ли в C++ вызвать функцию слева от знака "="?

Да, например, так:

#include <iostream>
using namespace std;

int &fun() {
 static int x; //с не-статической переменной м.б. небезопасно
 return x;
}

int main() {
 fun() = 10;
 cout << fun() << endl; //10
 cin.get(); return 0;
}
Можно ли в C++ получить доступ к приватным свойствам класса без использования методов-членов или функций-друзей класса?

Ага :) Указатели-то на что?

#include <iostream>
using namespace std;

class Test {
private:
 int data;
public:
 Test() { data = 0; }
 int getData() { return data; }
};

int main() {
 Test t;
 int *ptr = (int*)&t;
 *ptr = 10; //косвенно пишем в свойство data... приём опасный, но работает не хуже лома
 cout << t.getData(); //приватное свойство было изменено. Вот и вся инкапсуляция :)
 cin.get(); return 0;
}
Можно ли в C++ сделать класс, объекты которого можно будет создавать только динамически?

Угу. Сделав деструктор приватным, мы лишим компилятор возможности удалять созданные в стеке объекты, на что он будет ругаться ошибкой компиляции.

А для удаления динамических объектов кучи можно предусмотреть отдельную функцию-друг класса. Это будет платой за "защиту от стека".

#include <iostream>
using namespace std;

class Test {
private:
 ~Test() { cout << "Destroying Object\n"; }
public:
 Test() { cout << "Object Created\n"; }
 friend void destructTest (Test *);
};

void destructTest(Test* ptr) {
 delete ptr;
 cout << "Object Destroyed\n";
}

int main() {
 //Test t1; //теперь это вызовет ошибку компиляции

 Test *t2 = new Test(); //Всё хорошо
 destructTest (t2);
 
 cin.get(); return 0;
}
Можно ли в C++ сделать класс, объекты которого наоборот можно будет создавать только в стеке, но не динамически в куче?

Да. В противоположность предыдущему пункту, можно определить в классе приватный оператор new.

#include <iostream>
using namespace std;

class Test {
 void* operator new (size_t size){};
 int x;
public:
 Test(int x) { this->x = x; }
 void show() { cout << x << endl; }
 ~Test() { }
};

int main() {
 //Test* t1= new Test(1); //А теперь нельзя вот так
 
 Test t2(2); t2.show(); //Всё работает
 cin.get(); return 0;
}
Можно ли в C++ из пустой функции main что-нибудь вывести в консоль?

Да. Скажем, создав глобальный объект класса, который выводит что-то в консоль из конструктора.

#include <iostream>
using namespace std;

class Test {
public:
 Test() { cout << "Hello, world!"; }
} test;

void main() {}

Также класс можно заменить структурой или просто глобальной переменной:

#include <cstdio>

int var = printf ("Hello, world!");
void main() {}
Можно ли в C++ напечатать натуральный ряд чисел, не используя цикла или рекурсии?

Да. Можно использовать шаблоны класса и статические функции.

#include <iostream>
using namespace std;

template <int N> class Printer {
public:
 static void print()  {
  Printer<N - 1>::print();  //Это - не рекурсия, а вызов статического метода!
  cout << N << endl;
 }
};

template <> class Printer<1> {
public:
 static void print()  {
  cout << 1 << endl;
 }
};

int main() {
 const int N = 1000;
 Printer<N>::print();
 cin.get(); return 0;
}

...или просто массив объектов класса и статическую переменную.

#include<iostream>
using namespace std;

class Printer {
public:
 static int a;
 Printer() {
  cout << a++ << endl;
 }
};

int Printer::a = 1;

int main() {
 const int N = 1000;
 Printer obj[N];
 cin.get(); return 0;
}
Можно ли в C++ найти сумму всех цифр числа одним оператором?

Да, если это оператор цикла for

#include <iostream>
using namespace std;

int main() {
 int n = 6789, sum;
 for (sum = 0; n > 0; sum += n % 10, n /= 10); //один оператор
 cout << sum;
 cin.get(); return 0;
}

Можно ли в C++ писать URL-адреса прямо в тексте программы?

Да. Полную ссылку, которая начинается с названия протокола, можно вставить в текст программы без каких-либо символов-ограничителей.

#include <iostream>
using namespace std;

int main() {
http://www.disney.com

cout << "Hello, world!";
cin.get(); return 0;
}

Просто http: воспринимается как метка, а всё, что за символами // - как комментарий :)

Можно ли в C++ поменять местами значения 2 переменных без использования третьей переменной или арифметических операций?

Да. Есть ещё побитовые операции и операция следования (запятая).

#include <iostream>
using namespace std;

int main() {
 int x = -5, y = 10;
 (x ^= y), (y ^= x), (x ^= y);
 cout << x << " " << y;
 cin.get(); return 0;
}
Можно ли в C++ из пользовательской программы завершить работу системы?

Да, поскольку доступна функция system. Наверное, не буду выполнять этот код :)

#include <cstdlib>
using namespace std;

int main() {
 char *cmd = "c:\\windows\\system32\\shutdown /i"; //В кавычках - команда для Windows
 //или "shutdown -P now" для Linux
 system(cmd);
 return 0;
}

На самом деле, современные ОС попросят подтвердить действие.

Можно ли в C++ сравнить значения 2 переменных без использования оператора сравнения (==)?

Да. Побитовое исключающее "или".

#include <iostream>
using namespace std;

int main() {
 int x = 10, y = 10;
 cout << x << (!(x ^ y) ? "==" : "!=") << y;
 cin.get(); return 0;
}
Можно ли в C++ напечатать ряд натуральных чисел 1,2,...,N, не используя ни одной точки с запятой?

Рекурсивный вызов функции main позволяет решить эту задачу, например, так:

#include <iostream>
using namespace std;
int N = 10;

int main() {
 static int x = 1;
 if (cout << x << " " && x++ < N && main()) {} //точки с запятой при решении задачи нет!
 //cin.get(); //иначе придётся нажить Enter 10 раз, рекурсия же :)
 return 0;
}
Можно ли в C++ найти максимум или минимум из двух целочисленных значений, не используя условий или циклов?

Да.

#include <iostream>
using namespace std;

int main() {
 int a = -10, b = 10;
 cout << endl << "Max=" << ((a + b) + abs(a - b)) / 2;
 cout << endl << "Min=" << ((a + b) - abs(a - b)) / 2;
 cin.get(); return 0;
}
Можно ли в C++ сложить 2 числа, не используя операцию сложения?

Да. Есть же вычитание :)

#include <iostream>
using namespace std;

int main() {
 double a = -3.5;
 double b = 2.8;
 double sum = -(-a - b);
 cout << endl << "Sum=" << sum;
 cin.get(); return 0;
}
Можно ли в C++ выполнить обе ветви условного оператора "если-то-иначе"?

Да.

#include <iostream>
using namespace std;

int main() {
 if (!(cout << " 1 ")) { 
  cout << " 1 "; //Проверьте в отладчике, это тоже выполняется
 }
 else { cout << " 2 "; }
 cin.get(); return 0;
}
Можно ли в C++ умножать или делить целое значение на степени двойки, не используя операций "*" или "/"?

Это можно делать в любом языке, где есть побитовый сдвиг. Умножению значения x на 2, например, соответствует операция x << 1, а делению y на 4 - действие y >> 4. Не путайте операторы побитового арифметического сдвига с переопределёнными в некоторых классах (например, в классах поточного ввода-вывода) операторами вставки << и извлечения >>.

#include<iostream>
using namespace std;

int main() {
 int x = 4, y = -8;
 x = x << 1; //4*2==8
 y = y >> 2; //-8/4==-2
 cout << x << " " << y;
 cin.get(); return 0;
}
Можно ли в C++ проверить порядок байтов (big-endian/little-endian) компьютера?

Для многобайтовых величин имеет значение, в каком порядке хранятся байты числа.

Порядок байтов бывает big-endian (от старшего к младшему) и little-endian (от младшего к старшему).

Например, десятичное число 10000 равно 0x2710 в 16-ричном виде и "естественном" представлении big-endian, а в little-endian это будет 0x1027. Именно так может храниться значение на линейке процессоров x86 :)

#include <iostream>
using namespace std;

int main() {
 unsigned int n = 1;
 char *c = (char*)&n;
 if (*c) cout << "little-endian";
 else cout << "big-endian";
 cin.get(); return 0;
}
Как узнать максимальное значение для беззнакового целого типа?

Можно так, если не хочется искать константу, содержащую это значение:

#include <iostream>
using namespace std;

int main() {
 unsigned int max = 0;
 max = ~max;
 cout << endl << "unsigned int: " << max;
 unsigned long long int max2 = 0;
 max2 = ~max2;
 cout << endl << "unsigned long long int: " << max2;
 cin.get();
 return 0;
}
Можно ли в C++ найти знак числа без использования любого (бинарного или тернарного) условного оператора?

Так как результаты выполнения операций отношения можно рассматривать как числовые значения, то вот так:

#include <iostream>
using namespace std;

int sign (int n) { 
 return (n>0) - (n<0); //Скобки нужны из-за приоритетов!
}

int main() {
 cout << endl << sign(3); //1
 cout << endl << sign(-1); //-1
 cout << endl << sign(0); //0
 cin.get(); return 0;
}
Можно ли в C++ заполнить двумерную матрицу размерности n x m последовательно идущими числами 1,2,3,..., применяя только один цикл?

Остаток от деления и целочисленное деление C++ (целое поделить на целое дают целое) рулят.

Программка есть вот здесь.

У класса есть конструктор с одним целочисленным аргументом. Покажите три синтаксически различных способа создать экземпляр класса с помощью этого конструктора

Поможет "редкий" способ 3 для конструктора с одним аргументом.

#include <iostream>
using namespace std;

class Class {
 int n;
 public:
  Class (int n=0) { this->n= n; }
  void show() { cout << n << endl; }
};

int main() {
 Class object1(1); //Способ 1, обычный
 object1.show();
 Class object2 = Class (2); //Способ 2, так тоже можно
 object2.show();
 Class object3 = 3; //Способ 3, для конструктора с одним аргументом можно и так
 object3.show();
 cin.get(); return 0;
}
Как запретить автоматическое преобразование типов в конкструкторе (не создавать объекты способом 3 из предыдущего вопроса)?

Для того и нужно ключевое слово explicit

#include <iostream>
using namespace std;

class Class {
 int n;
 public:
  explicit Class (int n=0) { this->n= n; }
  void show() { cout << n << endl; }
};

int main() {
 Class object1(1); //Способ 1, обычный
 object1.show();
 Class object2 = Class (2); //Способ 2, так тоже можно
 object2.show();
 cin.get(); return 0;
}
Всегда ли при выполнении операции вида a = b, где a и b - объекты одного класса, вызывается конструктор копирования?

Неа, не всегда. Конструктор копирования применяется только к инициализациям, но не к присваиваниям.

/*
 Конструктор копирования применяется только к инициализациям, но не к присваиваниям
*/
#include <iostream>
using namespace std;

class Class {
 int n;
 public:
  Class (int n=0) { this->n= n; }
  Class (Class &that) {
   cout << "Constructor called" << endl;
   this->n = that.n;
  }
  void show() { cout << n << endl; }
  Class operator = (Class &that) {
   cout << "Operator = called" << endl;
   this->n = that.n;
  }
};

int main() {
 Class a(1);
 Class b = a; //Конструктор копирования будет вызван
 a.show(); b.show();
 Class c(2);
 c = a; //Конструктор копирования не вызывается, а переопределённый оператор "=" - да
 c.show();
 cin.get(); return 0;
}
Как определить для объектов класса сложение с числом в виде Obj+i и i+Obj?

Для решения задачи можно использовать, например, пару функций-"друзей" класса.

#include <iostream>
using namespace std;

class Class {
 int n;
 public:
  Class (int n=0) { this->n= n; }
  void show() { cout << n << endl; }
  friend Class operator + (Class & obj, int n);
  friend Class operator + (int n, Class & obj);
};

Class operator + (Class &obj, int n) {
 Class temp;
 temp.n = obj.n + n;
 return temp;
}

Class operator + (int n, Class & obj) {
 return obj+n;
}

int main() {
 Class a(1),b(2);
 Class c = a + 1; 
 c.show(); //2
 Class d = 1 + b; 
 d.show(); //3
 cin.get(); return 0;
}
Как получить доступ к элементам, определенным дочерним классом через указатель на базовый класс?

Даже не знаю, почему этот вопрос здесь... обычное, в общем, приведение типов :)

#include <iostream>
#include <string>
using namespace std;

class Base {
 string data;
 public:
  Base (string data="") { this->data= data; }
  virtual void show() { cout << endl << data; }
};

class Derived : public Base {
 int n;
 public:
  Derived (string data="", int n = 0) : Base(data) { this->n = n; }
  void show () { Base::show(); cout << ", " << n; }
  int get_n () { return n; }
};

int main() {
 Base a("Base");
 Base *ptr = &a; //Указатель на базовый класс
 ptr->show();
 Derived b ("Derived",1);
 ptr = &b; //Работает и присвоение адреса объекта дочернего класса
 ptr->show();
 cout << endl << ((Derived *)ptr)->get_n();
  //Доступ к элементам, определенным дочерним классом через указатель на базовый класс
 cin.get(); return 0;
}
Как классу "выдать себя" за своего предка, то есть, использовать его виртуальный метод?

Так как атрибут virtual передается по наследству через все уровни наследования, можно просто не переопределять на нужном уровне наследования метод, объявленный в предке виртуальным (show)

#include <iostream>
using namespace std;

class Base {
 public:
  virtual void show() { cout << endl << "Base"; }
};

class Derived1 : public Base {
 public:
  void show() { cout << endl << "Derived1"; }
};

class Derived2 : public Derived1 {
};

int main() {
 Base a;     a.show(); //Base
 Derived1 b; b.show(); //Derived1
 Derived2 c; c.show(); //Derived1, так как show не определён в Derived2
 cin.get(); return 0;
}
Как "заставить" класс-потомок определить виртуальный метод?

Определить в предке чисто виртуальную функцию (show). Иначе это называется "сделать класс абстрактным"

#include <iostream>
using namespace std;

class Base {
 public:
  virtual void show() =0;
};

class Derived1 : public Base {
 public:
  void show() { cout << endl << "Derived1"; }
};

class Derived2 : public Base {
};

int main() {
 Derived1 b; b.show(); //Derived1
// Derived2 c; //ошибка компиляции!
//Base a; // Теперь также нельзя создать объект класса Base
 Base *ptr = &b; ptr->show(); //но можно указатель на него
 cin.get(); return 0;
}
Как заставить шаблон функции с двумя аргументами обрабатывать по-другому один скалярный тип данных, чем другие сочетания типов?

Вариант 1. Написать для этого типа альтернативный шаблон функции

#include <iostream>
using namespace std;

template <class T1, class T2>
T1 divs (T1 a, T2 b) { return a/(T1)b; }

template <class T>
T divs (T a, T b) { return a/b; }

int main() {
 double a = 3.5, b = 2.;
 int c = 3, d = 2;
 cout << divs(a,b) << endl; //1.75
 cout << divs(a,c) << endl; //1.16667
 cout << divs((double)c,d) << endl; //1.5
 cout << divs(c,d) << endl; //1
 cin.get(); return 0;
}

Вариант 2. Написать для этого типа явно заданную функцию

#include <iostream>
using namespace std;

template <class T>
T divs (T a, T b) { return a/b; }

double divs (int a, int b) { return ((double)a)/b; }

int main() {
 double a = 3.5, b = 2.;
 cout << divs(a,b) << endl; //1.75
 int c = 3, d = 2;
 cout << divs(c,d) << endl; //1.5
 cin.get(); return 0;
}
Как инициализировать члены класса в конструкторе, не обращаясь к ним из тела функции-конструктора?

Список инициализации в конструкторе...

#include <iostream>
using namespace std;

class Class {
 int n,m;
 public:
  Class (int n, int m) : n(n), m(m) {}
  void show() { cout << n << " " << m << endl; }
};

int main() {
 Class obj (1,2);
 obj.show(); //1 2
 cin.get(); return 0;
}
Почему здесь выведется 2 единицы?
#include <iostream>
using namespace std;

int main() {
 int a = 0;
 decltype((a)) b = a;
 b++;
 cout << a << b; //11
 cin.get();
 return 0;
}

Если пропустить связанный с decltype нюанс, кажется, что должны вывестись 0 и 1 - b получила значение a, равного нулю, потом b увеличили на единицу.

На самом деле, примерно вот так (не уверен, что моя интерпретация на 100% верна):

1. decltype(var), когда var — это объявленная переменная (например в функции или как член класса). В этом случае decltype(var) будет иметь в точности тот тип, с которым объявлена переменная.

2. decltype(expr), expr — выражение. В этом случае типом decltype(expr) будет тип, которое могло бы вернуть это выражение, с той оговоркой, что decltype(expr) будет иметь тип T& (const T&), если expr возвращает lvalue, T, если expr возвращает rvalue типа Т (const T) и T&& (const T&&), если expr возвращает xvalue (rvalue reference).

То есть, в данном случае b будет ссылкой на a.

Штука на самом деле кошмарная:

int i;
decltype(i); // int
decltype(i + 1); // int
decltype((i)); // int&
decltype(i = 4); //int&
const int foo();
decltype(foo()) ;// int
int&& foo1();
decltype(foo1()) ;// int&&
Почему переопределённый оператор присваивания возвращает ссылку на объект?

Потому что должны работать операторы не только вида

x = y;

но и

if ((x = y)) {

и особенно

x = y = z;

(присваивание "по цепочке").

Вот пример, кроме того, ещё раз показываем, когда вызывается конструктор копирования, а когда оператор "="

#include <iostream>
using namespace std;

class Fraction {
 int n, d; //числитель и знаменатель
 int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } //Наибольший общий делитель
 
public:
 Fraction(int n = 1, int d = 1) : n(n / gcd(n, d)), d(d / gcd(n, d)) { } //Конструктор сразу сокращает дробь
 int num() const { return n; }
 int den() const { return d; }
 Fraction & operator *= (const Fraction& rhs) { //Домножение дроби на дробь
  int new_n = n * rhs.n / gcd(n * rhs.n, d * rhs.d);
  d = d * rhs.d / gcd(n * rhs.n, d * rhs.d);
  n = new_n;
  return *this;
 }
 Fraction (Fraction& rhs) { //Конструктор копирования создает новый объект копированием
  n = rhs.n; d = rhs.d;
 } 
 Fraction& operator = (const Fraction& rhs); //Прототип функции для "=", работает при инициализации
};

ostream & operator << (ostream & out, const Fraction & f) { //Вывод дроби в ostream
 return out << f.num() << '/' << f.den();
}
bool operator == (const Fraction & lhs, const Fraction & rhs) { //Сравнение дробей
 Fraction _lhs(lhs.num(), lhs.den());
 Fraction _rhs(rhs.num(), rhs.den()); // 1/2 == 4/8 !
 return _lhs.num() == _rhs.num() && _lhs.den() == _rhs.den();
}
bool operator != (const Fraction& lhs, const Fraction& rhs) {
 return !(lhs == rhs);
}
Fraction operator * (Fraction lhs, const Fraction & rhs) { //Умножение дробей
 return lhs *= rhs;
}

Fraction & Fraction::operator = (const Fraction & rhs) { //Присваивание дробей
 int gcd = this->gcd(rhs.n, rhs.d);
 n = rhs.n / gcd;
 d = rhs.d / gcd;
 return *this;
}

int main() {
 Fraction f1(4, 8), f2(1, 2), f3(10, 2);
 cout << f1 << " * " << f2 << " = " << f1 * f2 << endl // 1/4
  << f2 << " * " << f3 << " = " << f2 * f3 << endl // 5/2
  << 2 << " * " << f1 << " = " << 2 * f1 << endl; // 1/1
 cout << (f1==f2 ? "f1==f2" : "f1!=f2") << endl; // f1==f2
 Fraction f4 = f2; //Конструктор копирования
 cout << "f4=" << f4 << endl; // 1/2
 Fraction f5;
 f5 = f4 = f3; //Оператор "=" работает по цепочке!
 cout << "f3=" << f3 << endl // 5/1
  << "f4=" << f4 << endl // 5/1
  << "f5=" << f5 << endl; // 5/1
	cin.get(); return 0;
}

По сути, если переопределённый "=" не будет возвращать ссылку на объект, то компилятор будет запускать конструктор копирования и операция будет соответствовать не присваиванию, а инициализации. А вот когда мы возвращаем ссылку на объект, тогда конструктор копирования не запускается и получается операция присваивания.

Не забываем также, что операторы =, (), [], -> должны быть функциями-членами класса.

Как разрешить инициализацию нескольких членов класса в виде X x{ 1, 2 } ?

Например, оставив класс тривиальным.

Наличие конструктора по умолчанию с пустым телом автоматически делает класс нетривиальным, отсюда вытекает то, что тип становится не POD и нельзя использовать агрегатную инициализацию.

При определении конструктора с ключевым словом = default тривиальность класса сохраняется, если она была до этого. Это равносильно отсутствию явного упоминания конструктора в определении класса.

Если конструктор по умолчанию определен как = default вне определения класса, всё равно будет считаться, что конструктор предоставлен пользователем, и это тоже делает класс нетривиальным.

Пример кода:

#include <iostream>

class X {
 public:
 //X() {}
 X() = default;
 int prop1, prop2;
};

int main() {
 X x{ 1, 2 }; // ошибка, если раскомментарить дефолтный конструктор и убрать X() = default;
}
Когда уместно писать код в файле .h, а в .cpp не стоит?

Когда пишем шаблонный класс.

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

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

Вот этот код синтаксически вполне правилен.

Файл main.cpp
#include "myclass.h"
using namespace std;

int main() {
 myclass <int> i(3); i.show();
 myclass <char> c('c'); c.show();
 return 0;
}
Файл myclass.h
#include <iostream>

template <class T> class myclass {
 T bar;
public:
 myclass (T);
 T foo();
 void show();
};
Файл myclass.cpp
#include "myclass.h"

template <class T> myclass <T>::myclass(T bar) {
 this->bar = bar;
}

template <class T> T myclass <T>::foo() {
 return this->bar;
}

template <class T> void myclass <T>::show() {
 std::cout << this->bar << std::endl;
}

Но в Studio получим пресловутую "error LNK2019", а в QT "undefined reference".

Если убрать myclass.cpp, а myclass.h превратить в

#include <iostream>

template <class T> class myclass {
 T bar;
public:
 myclass (T bar) {
  this->bar = bar;
 }
 T foo() {
  return this->bar;
 }
 void show() {
  std::cout << this->bar << std::endl;
 }
};

то всё везде работает.

Можно ли в C++ прибавить к целому числу единицу, не используя арифметических операций сложения, вычитания или инкремента и декремента?

Побитовые операции никто не отменял.

#include <iostream>
using namespace std;
int main() {
 int x = 12;
 int y = (-(~x));
 cout << y;
 cin.get(); return 0;
}

теги: программирование c++ список тест

06.03.2018, 17:06; рейтинг: 1955