Страницы

Поиск по вопросам

вторник, 26 марта 2019 г.

Не доступен protected член из подкласса в другом пакете

Почему возникает ошибка в закомментированной строчке, ведь protected челны класса, доступны классам, находящимся в том же пакете,и подклассам — в других пакетах.
package one; public class One { protected int val; }
Если такой же класс создать в пакете package one, то в закомментированной строке ошибки уже не будет. Для static переменной в обоих случаях ошибки не будет.
package two; import one.One; public class Two extends One{ private One o = new One(); //private int val = o.val; }


Ответ

В дополнение к этому правильному ответу, несколько слов о мотивации.
Если бы производный класс имел бы доступ к protected-полям любого объекта базового типа, то было бы очень легко обойти инкапсуляцию. Действительно, пусть у вас есть объект типа A, и protected-поле x. Тогда вы из любой точки кода смогли бы получить доступ к x, используя такой простой трюк:
class FakeA extends A { public void SetXFor(A obj, int x) { obj.x = x; } }
new FakeA().SetXFor(obj, 42);
Чтобы такие трюки были невозможны, доступ к protected-членам и ограничен текущим экземпляром объекта.

Java. Почему нельзя изменить ссылку на объект в методе?

Мой педагог задал мне задание ответить на ряд вопросов. И два их них меня сильно смущают.
Почему нельзя изменить ссылку на объект в методе?
Не могу понять смысл вопроса(((
Как в Java передаются параметры в методы?
Я гуглил этот вопрос и понял что по всегда по значению но что точно это значит не понял.
Не могли бы вы мне помочь разобраться в этой ситуации.


Ответ

Почему нельзя изменить ссылку на объект в методе?
Потому что в метод передается не ссылка, а объект, который содержит копию ссылки
Как в Java передаются параметры в методы?
Все аргументы (как примитивные типы, так и объекты) в Java передаются при вызове по значению. Тут сразу стоит оговориться, что объект по сути представляет ссылку, следовательно, при изменении объекта в методе, изменится и исходный объект.

Подробнее можно почитать в «Подробное рассмотрение особенностей передачи аргументов» в полном руководстве по Java авторства Герберта Шилдта.

Что делают указатели в приведенном методе?

Пример:
// Copies count bytes from src to dst. The source and destination // blocks are permitted to overlap. public static void Copy(void* src, void* dst, int count) { byte* ps = (byte*)src; byte* pd = (byte*)dst; if (ps > pd) { for (; count != 0; count--) *pd++ = *ps++; // Тут } else if (ps < pd) { for (ps += count, pd += count; count != 0; count--) *--pd = *--ps; // И тут } }
Для меня понятно что именно делает данный метод. Для меня непонятно шаманство с указателями. Почему например в else if используется префикс, а в if постфикс? Можете разъяснить подробно?


Ответ

Все дело в инварианте цикла.
Когда идет побайтовое копирование "вперед" - указатели указывают на первый байт еще не обработанной области памяти:
----dddddddd---- ----ssssssss---- ^ ^ | | pd ps
Поэтому, на каждом шаге цикла происходит сначала копирование - потом сдвиг указателей:
*pd = *ps; pd = pd + 1; ps = ps + 1;
Запись *pd++ = *ps++ является более коротким способом записать три строчки выше. Используемая тут операция - постинкремент - увеличивает переменную, возвращая при этом старое значение переменной.
При копировании в обратную сторону - так получается, что указатель указывает на байт, идущий сразу после необработанного блока памяти:
----ssssssss---- ----dddddddd---- ^ ^ | | ps pd
Поэтому, на каждой итерации цикла его сначала сдвигают - а потом уже делают присваивание:
ps = ps - 1; pd = pd - 1; *pd = *ps;
Более коротко это записывается как *--pd = *--ps. используемая операция - предекремент - уменьшает переменную, возвращая новое значение переменной.

Указатели в Си, функции

Добрый день господа, собственно вопрос. Вижу периодически, что в некоторых функциях указатели вставляют то таким образом: function(&name), то таким: function(name)
Может кто нибудь объяснить, что да как с этим делом? Я понимаю, как работают указатели, но чёт догнать не могу, почему в функции стоит входной параметр char, а отправляют получается адрес указателя. И если можно, пример для использования возможных вариантов. Заранее спасибо!


Ответ

В выражениях массивы за редким исключением неявно преобразуются в указатель на свой первый элемент. Поэтому когда вы передаете массив в функцию, как в нижеприведенном примере
void f( int *a, size_t n ) { for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] ); printf( "
" ); }
int a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
f( a, 10 );
то не надо указывать перед именем массива оператор &. Массив уже преобразован в указатель типа int *
Однако если вы имеете дело со скалярными объектами, как, например, выражение a[0], которое представляет собой первый элемент массива, то есть скалярный объект со значением 0, как следует из определения массива в примере выше, то если вы хотите передать его адрес в вышеуказанную функцию , то вам надо будет записать
f( &a[0], 10 );
Фактически, данные два вызова
f( a, 10 );
и
f( &a[0], 10 );
эквивалентны, так как в обоих случаях передается адрес на первый элемент массива.
В виду этого данные объявления функции эквивалентны и объявляют одну и ту же функцию
void f( int a[10], size_t n ) void f( int a[20], size_t n ) void f( int a[], size_t n ) void f( int *a, size_t n )
Вы все эти объявления можете одновременно включить в программу, и программа будет успешно компилироваться.
То же самое справедливо и для символьных массивов. Имейте в виду, что строковые литералы также имеют тип массивов. И если имеется вызов вида
h( "Hello" );
где h - это некоторая функция, то в функцию передается адрес первого символа литерала, так как, как описано выше, данный литерал, который представляет из себя символьный массив, неявно преобразуется в указатель на свой первый символ.
В заключение приведу демонстрационную программу, в которую также включен пример, показывающий, что строковые литералы - это массивы.
#include
void f( int a[10], size_t n ); void f( int a[20], size_t n ); void f( int a[], size_t n ); void f( int *a, size_t n );
void f( int *a, size_t n ) { for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] ); printf( "
" ); }
void h( char c ) { printf( "%c
", c ); }
int main(void) { int a[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
f( a, 10 ); f( &a[0], 10 );
h( "Hello"[0] );
return 0; }
Вывод программы на консоль
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 H

Как разрешить циклические зависимости в Делфи?

Допустим у нас есть 2 класса, расположенных в разных юнитах:
TMaster - хозяин собаки, который должен о ней знать TDog - собака, которая должна знать о своем хозяине
Unit_Master:
uses Unit_Dog; type TMaster = class public Dog: TDog; end;
Unit_Dog:
uses Unit_Master; type TDog = class public Master: TMaster; end;
Использование таких прямых ссылок между классами очень облегчило бы жизнь. К сожалению, Delphi не может скомпилировать такой код из-за циклической зависимости между юнитами.
Как грамотно разрулить циклическую зависимость и скомпилировать подобный код, какие есть способы?


Ответ

Вариант №4
Использовать помощники классов.
(Способ через private официально перестал работать с Delphi Seattle (но есть пути как обойти: https://stackoverflow.com/questions/37351215) и, вероятно, можно будет перейти на protected)
Unit_Helper:
uses Unit_Master, Unit_Dog; type TMasterHelper = class helper for TMaster private function GetDog: TDog; procedure SetDog(aObject: TDog); public property Dog: TDog read GetDog write SetDog; end;
TDogHelper = class helper for TDog private function GetMaster: TMaster; procedure SetMaster(aObject: TMaster); public property Master: TMaster read GetMaster write SetMaster; end;
function TMasterHelper.GetDog: TDog; begin Result := fDog; end;
Unit_Master:
type TMaster = class protected fDog: TObject; end; uses Unit_Helper; .. Dog.Bark;
Unit_Dog:
type TDog = class protected fMaster: TObject; end; uses Unit_Helper; .. Master.Yell;
Минусы в том, что у класса может быть максимум 1 помощник, следовательно воспользоваться этим трюком можно только 1 раз.

Сократить немного код

Можно ли как-то преобразовать в более короткое решение? Как-то по-хитрому вынести в метод?
btnSignup.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(LoginActivity.this, SignUpActivity.class)); } });
btnReset.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { startActivity(new Intent(LoginActivity.this, ForgotPasswordActivity.class)); } });


Ответ

Подключите либу Retrolambda. Так вы сможете писать код в стиле 8 явы. Ваш код превратится в это:
btnSignup.setOnClickListener(v -> startActivity(new Intent(LoginActivity.this, SignUpActivity.class)));
btnReset.setOnClickListener(v -> startActivity(new Intent(LoginActivity.this, ForgotPasswordActivity.class)));

Правильное использование ограничений в обобщённых типах

Есть интерфейс IRepository:
public interface IRepository where T : BaseModel {...}
где BaseModel- базовый класс для моделей. Есть провайдер к базе данных, который реализует этот интерфейс:
public class Provider : IRepository where T : BaseModel {...}
Есть ли необходимость указывать ограничение на T в случае провайдера, если оно уже указано на уровне интерфейса?


Ответ

Ну очень странный вопрос. Да нужно, потому что без него не компилируется:
http://ideone.com/zfhSDo
public class Provider : IRepository /* where T : BaseModel */ {}
prog.cs(6,14): error CS0314: The type `T' cannot be used as type parameter `T' in the generic type or method `IRepository'. There is no boxing or type parameter conversion from `T' to `BaseModel' prog.cs(5,30): (Location of the symbol related to previous error)
http://ideone.com/I26aUP
public class Provider : IRepository where T : BaseModel {}
А тут всё хорошо.

Общий вопрос по работе *_cast< >()?

Привет всем.
Нужно разобраться как работает static_cast, dynamic_cast, да и вообще любой cast из серии кастов, но особенно эти 2, чтобы потом далее без помощи думать над casts.
На сколько смог вычитать, static_cast занимается тем, что берет и побитово( или все же побайтово?) копирует данные одного типа в данные другого типа. Вроде просто, но мне еще интересна реализация. Как найти релизацию? В visual studio я не могу перейти по goToDeclaration/defenition к описанию тела этой функции. *_cast вообще функция или что это? Похоже на какую-то типа шаблонную функцию или что-то типа generic в C#.
dynamic_cast. Преобразование с проверкой. То есть на сколько понял он позволяет делать upCast/downCas, но не позволяет делать crossCast. Тут у меня холиварный большой вопрос: зачем делать upCast - понятно, на этом работает полиморфизм. А вот downCast зачем в С++ нужен? Я пытался на stackowerflow найти ответ, читая посты с подобным вопросом на c#, но я пока не могу думать глобальными концепциями c#, потому мало что понял.
Можно еще приводить один тип объектов к другому (crossCast). Ну это для меня вообще дремучий лесище. Во-первых: указатели-то может как-то и приведутся, но а вообще как это будет работать-то??? Для этого нужно знать как вообще работают недра вызова методов (про раннее и позднее связывание знаю). Во-вторых: а зачем такое может быть нужно??? Можно пример попроще? В-третьих: я так понимаю, что после приведения одного объекта к другому, поля перемешаются и будут содержать мусор. Так как информация о том как приводить не имеется, на сколько я знаю, в классе нет такой информации. Ну а методы вообще непонятно как будут работать.
Помогите пожалуйста залатать столь огромную дыру в моих знаниях)


Ответ

На сколько смог вычитать, static_cast занимается тем, что берет и побитово (или все же побайтово?) копирует данные одного типа в данные другого типа.
Ни одно из преобразований в С++ не работает "побитово" или "побайтово". Потому они и называются преобразованиями. Преобразования выполняют конвертирование данных из одного формата в другой (зачастую - нетривиальное). В общем случае с "побитовой" точки зрения представление результата такого преобразования не имеет ничего общего с представлением исходного значения.
Побитовым/побайтовым копированием занимается функция memcpy, а не преобразования.
static_cast - пожалуй самый универсальный каст. Он умеет делать арифметические преобразования, преобразования указателей в/из типа void *, преобразования объектных указателей вверх-вниз по иерархии, преобразования указателей на члены вверх-вниз по иерархии и т.п.
Как найти релизацию?
Реализацию чего? Все преобразования являются операторами языка и, соответственно, реализуются на уровне ядра языка.
dynamic_cast. Преобразование с проверкой. То есть на сколько понял он позволяет делать upCast/downCas, но не позволяет делать crossCast.
Тут у вас наблюдается какая-то терминологическая путаница. Наоборот, dynamic_cast - это единственный из кастов, который умеет делать cross-cast. Cross-cast-ом называется прямое преобразование от одного базового подобъекта к другому базовому подобъекту в рамках одного общего содержащего объекта. Т.е. сross-cast может возникать только в условиях множественного наследования
#include
struct B { virtual ~B() {} }; struct C {}; struct D : B, C {};
int main() { D d; B *b = &d; // <- upcast C *c1 = &d; // <- upcast C *c2 = dynamic_cast(b); // <- cross-cast assert(c1 == c2); }
Также dynamic_cast умеет делать downcast сквозь виртуальное наследование (чего не умеет делать static_cast)
#include
struct A { virtual ~A() {} }; struct B : virtual A {}; struct C : virtual A {}; struct D : B, C {};
int main() { D d; A *a = &d; // <- upcast
D *d1 = dynamic_cast(a); // <- downcast, OK assert(d1 == &d);
D *d2 = static_cast(a); // <- downcast, ERROR }
dynamic_cast предназначен для нетривиальных полиморфных преобразований, но только внутри связной иерархии полиморфных классов. Ну и, разумеется, dynamic_cast умеет проверять корректность downcast-ов и сross-cast-ов.
А вот downCast зачем в С++ нужен?
Во многих жизненных ситуациях избежать downcast-а не удается, хоть с пьюристско-педантичной токи зрения downcast-ы возможно и идут вразрез с принципами чистого ООП. Во многих случаях код с downcast-ом получается проще, чем та же функциональность, реализованная с упорным избежанием downcast-а.
Одним из классических примеров идиоматического применения downcast является Curiously Recurring Template Pattern ("странно повторяющийся шаблон").
Можно еще приводить один тип объектов к другому (crossCast).
Приведение типов между посторонними несвязанными типами не является и не называется cross-cast-ом.
Делать приведение типов между несвязанными между собой типами может только reinterpret_cast, но по поводу результата такого приведения и его полезности почти никаких гарантий языком не дается.
В-третьих: я так понимаю, что после приведения одного объекта к другому, поля перемешаются и будут содержать мусор.
Так приводить можно не сами объекты, а только указатели или ссылки на них. Такое преобразования делает именно и только reinterpret_cast. И да, при доступе через такие приведенные указатели или ссылки получится ерунда. Но лазить в данные через такие приведенные указатели язык все равно запрещает - поведение не определено (за редкими исключениями). Так что это функциональность из серии "сам виноват".

Не вызывается деструктор у класса

Доброго времени суток. Имеется класс, который изменяет кодировку консоли. Код:
#include "stdafx.h" #include #include
class edit_locale { int old_cin; int old_cout; public: edit_locale(int); ~edit_locale(); };
int main() { edit_locale(1251); std::cout << "Иван" << std::endl;
return 0; }
edit_locale::edit_locale(int cp) { this->old_cin = GetConsoleCP(); this->old_cout = GetConsoleOutputCP(); SetConsoleCP(cp); SetConsoleOutputCP(cp); }
edit_locale::~edit_locale() { SetConsoleCP(this->old_cin); SetConsoleOutputCP(this->old_cout); }
Проблема заключается в следующем: функция SetConsole срабатывает только в конструкторе, в main - функции изменений не происходит. И деструктор не вызывается. С чем это может быть связано? Спасибо.


Ответ

Проблема в строчке
edit_locale(1251);
Вы создаёте временный объект, который тут же, в этой же строчке уничтожается.
Пишите так:
edit_locale l(1251);

Заполнение цветом клеток поля шахматной доски

Код написал только для нечетных строк:
// присваиваю в переменную table первый дочерний элемент body //таблицу "

" var table = document.body.children[0]; //запускаю первый цикл для столбцов, то есть [i] for (var i = 0; i < table.rows.length; i++) { //цикл для ячеек в строках, то есть [j] for (var j = 0; j < table.rows.length; j++) { //меняю фон ячеек на черный table.rows[i].cells[j += 1].style.backgroundColor = 'black'; } }

Как правильно заполнить доску для всех строк?


Ответ

var table = document.getElementById("board"); for (var i = 0; i < table.rows.length; i++) { for (var j = 0; j < table.rows[i].cells.length; j = j + 2) { table.rows[i].cells[((i + 1) % 2) + j].style.backgroundColor = 'black'; } } td { width: 30px; height: 30px; }


Проверка на вхождение элемента в массив

Добрый вечер! Есть двумерный массив arr и некорректная проверка на вхождение цифры 1.
byte[,] arr = new byte[,]{{1, 1, 1}, {1, 1, 1}}; // массив arr
if (arr.Exists (1)) { // проверка на вхождение
// ...
}
Как правильно записать условие?


Ответ

Вариант с расширяющим методом
public static class SquareArrayExtension { public static bool Contains(this byte[,] array, byte value) { for (int i = 0; i < array.GetLength(0); i++) { for (int j = 0; j < array.GetLength(1); j++) { if (value == array[i, j]) { return true; } } }
return false; } }
Пользоваться так
static void Main(string[] args) { byte[,] array = { { 1, 1, 1}, { 1, 1, 1} };
bool exists = array.Contains(5);
Console.WriteLine(exists);
Console.ReadLine(); }