1. Передача по ссылке
template
void process_2d_array_template(int (&array)[rows][cols])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
В C ++ передача массива по ссылке без потери информации о размерах, вероятно, является самой безопасной, так как не нужно беспокоиться о том, что вызывающий абонент передает неправильное измерение (флаги компилятора при несоответствии). Однако это невозможно с динамическими (freestore) массивами; он работает только для автоматических ( обычно стек-живых ) массивов, т. е. размерность должна быть известна во время компиляции.
2. Pass by pointer
void process_2d_array_pointer(int (*array)[5][10])
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < 5; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << (*array)[i][j] << '\t';
std::cout << std::endl;
}
}
С-эквивалент предыдущего метода передает массив по указателю. Это не следует путать с передачей по разлагаемому типу указателя массива (3), который является распространенным, популярным методом, хотя и менее безопасным, чем этот, но более гибким. Как и (1), используйте этот метод, когда все размеры массива фиксированы и известны во время компиляции. Обратите внимание, что при вызове функции адрес массива должен быть передан process_2d_array_pointer(&a)
, а не адрес первого элемента путем распада process_2d_array_pointer(a)
.
Они наследуются от C но менее безопасны, компилятор не имеет возможности проверить, гарантируя, что вызывающий абонент передает необходимые размеры. Функция только банков на том, что вызывающий абонент входит в качестве измерения (измерений). Они более гибкие, чем предыдущие, поскольку массивы различной длины могут быть переданы им неизменно.
Следует помнить, что нет такой вещи, как передача массива непосредственно функции в C [в то время как в C ++ они могут быть переданы как ссылка (1)]; (2) передает указатель на массив, а не сам массив. Всегда передавать массив as-is становится операцией копирования-указателя, которая облегчается природой массива для разложения в указатель .
3. Pass by (value) указатель на затухающий тип
// int array[][10] is just fancy notation for the same thing
void process_2d_array(int (*array)[10], size_t rows)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < 10; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
Хотя разрешено int array[][10]
, я бы не рекомендовал его над указанным выше синтаксисом, поскольку приведенный выше синтаксис дает понять, что идентификатор array
является единственным указателем на массив из 10 целых чисел, в то время как этот синтаксис выглядит , как будто это 2D-массив, но является тем же самым указателем на массив из 10 целых чисел. Здесь мы знаем количество элементов в одной строке (т. Е. Размер столбца, здесь 10), но количество строк неизвестно и, следовательно, должно быть передано в качестве аргумента. В этом случае есть определенная безопасность, поскольку компилятор может указывать, когда передается указатель на массив со вторым измерением, не равным 10. Первое измерение является изменяющейся частью и может быть опущено. См. здесь обоснование о том, почему разрешено только первое измерение.
4. Пропустить указатель на указатель
// int *array[10] is just fancy notation for the same thing
void process_pointer_2_pointer(int **array, size_t rows, size_t cols)
{
std::cout << __func__ << std::endl;
for (size_t i = 0; i < rows; ++i)
{
std::cout << i << ": ";
for (size_t j = 0; j < cols; ++j)
std::cout << array[i][j] << '\t';
std::cout << std::endl;
}
}
Опять есть альтернативный синтаксис int *array[10]
, который совпадает с int **array
. В этом синтаксисе [10]
игнорируется, поскольку он распадается на указатель, тем самым становясь int **array
. Возможно, это всего лишь сигнал вызывающему, что переданный массив должен иметь не менее 10 столбцов, даже если требуется подсчет строк. В любом случае компилятор не указывает на какие-либо нарушения длины и размера (он проверяет, является ли переданный тип указателем на указатель), поэтому для определения значения параметра строки и столбца требуется значение параметра
Примечание: (4) является наименее безопасным вариантом , поскольку он вряд ли имеет проверку типа и наиболее неудобен. Нельзя законно передать 2D-массив этой функции; C-FAQ осуждает обычное обходное решение для выполнения int x[5][10]; process_pointer_2_pointer((int**)&x[0][0], 5, 10);
, поскольку оно может потенциально привести к неопределенному поведению из-за сглаживания массива. Правильный способ передачи массива в этом методе приводит нас к неудобной части, то есть нам нужен дополнительный (суррогатный) массив указателей, каждый из которых указывает на соответствующую строку фактического, подлежащего передаче массива; этот суррогат затем передается функции (см. ниже); все это для того, чтобы выполнить ту же работу, что и вышеупомянутые методы, которые более безопасны, чище и, возможно, быстрее.
Вот программа драйвера для проверки вышеперечисленных функций:
#include
// copy above functions here
int main()
{
int a[5][10] = { { } };
process_2d_array_template(a);
process_2d_array_pointer(&a); // <-- notice the unusual usage of addressof (&) operator on an array
process_2d_array(a, 5);
// works since a's first dimension decays into a pointer thereby becoming int (*)[10]
int *b[5]; // surrogate
for (size_t i = 0; i < 5; ++i)
{
b[i] = a[i];
}
// another popular way to define b: here the 2D arrays dims may be non-const, runtime var
// int **b = new int*[5];
// for (size_t i = 0; i < 5; ++i) b[i] = new int[10];
process_pointer_2_pointer(b, 5, 10);
// process_2d_array(b, 5);
// doesn't work since b's first dimension decays into a pointer thereby becoming int**
}
Если предположить, что отношения являются односторонними, от U1 до U2, то все просто:
select u.*
from relationships r
inner join users u on r.u2_fk = u.id
where r.u1_fk = ?
Я понятия не имею, что означает действие или статус.
Что-то, что сделало бы это более понятным, это если вы сделаете свое поле дБ более описательным в таблице отношений: то есть user_id и friend_id. Тогда становится очевидным, что вы хотите присоединить файл Relations.friend_id к user.id и отфильтровать по параметру Relations.user_id
Затем вы можете дополнительно выполнить фильтрацию по статусу и т. Д. добавив желаемые результаты, я вижу, что вы хотите включить в результаты как u1_fk, так и u2_fk. Это, конечно, можно сделать, но вы делаете это гораздо более запутанным, ИМО.
Я бы предложил, чтобы, когда дружба была принята, создала новую линию для нового друга. Таким образом, когда вы хотите найти всех друзей для пользователя, вам нужно только сопоставить u1_fk (то есть, user_id), а другом всегда будет u2_fk (friend_id).
Затем каждый друг может независимо контролировать, как они хотят общаться, т.е. вы можете заблокировать меня или я могу заблокировать вас. - мои два цента стоят. Заставьте свою ассоциацию делать одно.
Может быть полезно взглянуть на схему:
User Relationship User
id
id ----> u1_fk
u2_fk <------- id
Это было бы легко понять. Но то, что вы хотите из этого, больше похоже на
User Relationship User
id
id -----> u1_fk <------ id
\---> u2_fk <---/
Итак, для начала, я смотрю на это с точки зрения фильтрации по отношениям, а затем добавляю информацию о пользователях по мере их соответствия:
select r.id, r.u1_fk, r.u2_fk,
coalesce(u1.user_id, u2.user_id) userid,
coalesce(u1.username, u2.username) username,
coalesce(u1.user_picture, u2.user_picture) picture
from relationship r
left join user u1 on r.u1_fk = u1.id
left join user u2 on r.u1_fk = u2.id
where u1_fk = ? or u2_fk = ?
но это означает, что вам придется объединять поля из u1 и u2.
Другое решение состояло бы в том, чтобы начать с пользователя, объединить все возможные отношения и объединить его с пользователями, которые не являются пользователем, с которым вы начали (элегантное решение GMB)
Однако, что происходит если у вас есть:
id u1_fk u2_fk
1 2
2 1
Я думаю, что оба решения дадут вам дублирующий список.
Вот почему я настоятельно рекомендую вам переименовать (по крайней мере, в вашей голове) u1_fk
и u2_fk
в user_id
и friend_id
.