Страницы

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

Показаны сообщения с ярлыком ссылки. Показать все сообщения
Показаны сообщения с ярлыком ссылки. Показать все сообщения

понедельник, 2 марта 2020 г.

Как сделать блок ссылкой?

#javascript #html #jquery #ссылки


Заказчику срочно понадобилось, чтобы блок стал ссылкой, но внутри этого блока есть
и куча разметки, в том числе, и самостоятельных тегов . В результате, если обернуть
весь блок этим тегом, браузер просто выкинет его оттуда, и будет отдельно ссылка и
отдельно блок. Каким образом можно сделать блок со сложной разметкой ссылкой, чтобы
не поссорить сайт с поисковыми системами?
    


Ответы

Ответ 1



.outer{ position:relative; background: green; height: 200px; } .direct-link { position:relative; z-index:2; background: red; } .all-block-link { position:absolute; z-index:1; width:100%; height:100%; left:0; top:0; }
Попробуй сделать следующее

Ответ 2



попробуйте так: $("#block").click(function(){ location.href="нужный url" }); $("#block a").click(function(e){e.stopPropagation()});

понедельник, 24 февраля 2020 г.

invalid initialization of non-const

#cpp #классы #ссылки #перегрузка_операторов


#include 
#include 

using namespace std;

class complex {
    double re, im;
public:
    complex(double = 0, double = 0);
    ~complex();

    complex operator+(complex&);
    complex operator-(complex&);
    complex operator*(complex&);
    complex operator/(complex&);
    complex operator^(unsigned);
    friend istream& operator >> (istream &, complex&);
    friend ostream& operator << (ostream &, complex &);
};

complex::complex(double r, double i)
{
    re = r;
    im = i;
}

complex::~complex() { }

complex complex::operator+(complex&y)
{
    return complex(re + y.re, im + y.im);
}

complex complex::operator-(complex&y)
{
    return complex(re - y.re, im - y.im);
}

complex complex::operator*(complex&y)
{
    return complex(re*y.re - im*y.im, re*y.im + im*y.re);
}

complex complex::operator/(complex&y)
{
    double r1 = re;
    double i1 = im;
    double r2 = y.re;
    double i2 = y.im;
    return complex((r1*r2 - i1*i1) / (r2*r2 + i2*i2), (-r1*i2 + i1*r2) / (r2*r2 +
i2*i2));
}

complex complex::operator^(unsigned n)
{
    complex y(1, 0);
        for (int i = 1; i <= n; i++)
            y = y*(*this);
    return y;
}

istream& operator >> (istream& is, complex& x)
{
    char c;
    cin >> c;
    assert(c == '(');
    cin >> x.re;
    cin >> c;
    assert(c == ',');
    cin >> x.im;
    cin >> c;
    assert(c == ')');
    return is;

}

ostream& operator<<(ostream&os, complex&x)
{
    os << '(' << x.re << ',' << x.im << ')' << endl;
    return os;
}

int main(){

    complex a(1, 1);
    complex b(1, 1);
    complex c(1, 1);
    complex x;

    cout << "Введите комплексное число в формате: (re,im) ->";
    cin >> x;
    cout << "Результат =" << a*(x ^ 2)) + b*x + c << endl;
    return 0;
}


На линуксе выводит ошибку:


  invalid initialization of non-const


для строки:

cout << "Результат =" << a*(x ^ 2) + b*x + c << endl;


Саму программу скопировал с Windows, ошибки исправил, только эту выдает. В чем здесь
проблема? И как можно ее решить? На Visual Studio работала нормально.
    


Ответы

Ответ 1



Объявите данные операторы следующим образом complex operator+( const complex&) const; complex operator-(const complex&) const; complex operator*(const complex&) const; complex operator/( const complex&) const; complex operator ^( unsigned int ) const; friend ostream& operator << (ostream &, const complex &); Нельзя связывать временные объекты с неконстантной lvalue ссылкой. Обратите внимание , что конструктор класса является преобразующим конструктором. Поэтому у вас будет работать выражение complex + double, как, например, complex a( 10, 10 ); complex b = a + 10; Но не будет работать выражение double + complex complex a( 10, 10 ); complex b = 10 + a; // ошибка компиляции так как ваши операторы являются членами класса. Вы могли бы их объявить как отдельные функции, не являющиеся членами класса, которые принимают два аргумента. Например, friend complex operator +( const complex &lhs, const complex &rhs ) { return complex( lhs.re + rhs.re, lhs.im + rhs.im ); } В этом случае второе выражение было бы также верным. Эта дружественная функция может быть определена как в самом классе, и тогда при использовании оператора будет применен зависящий от аргумента поиск (ADL), либо вне класса. Что касается Visual Studio, то его компилятор имеет собственные расширения языка (которые активизированы по умолчанию; вы можете отключить эту опцию в настройках компилятора, и тогда будет выдано сообщение об ошибке), которые порой противоречат стандарту C++. Не стоит полагаться на эти расширения языка компилятора C++ Visual Studio, так как код будет не переносимым. EDIT: Так как у вас возникают трудности правильно набрать код, то приведу для вас рабочий вариант программы. Я не проверял, определены ли правильно операторы с точки зрения математики. #include #include class complex { private: double re, im; public: complex( double = 0, double = 0 ); ~complex(); complex operator +( const complex & ) const; complex operator -( const complex & ) const; complex operator *( const complex & ) const; complex operator /( const complex & ) const; complex operator ^( unsigned int ) const; friend std::istream & operator >>( std::istream &, complex & ); friend std::ostream & operator <<( std::ostream &, const complex & ); }; complex::complex( double r, double i ) : re( r ), im( i ) { } complex::~complex() { } complex complex::operator +( const complex &c ) const { return complex( re + c.re, im + c.im ); } complex complex::operator -( const complex &c ) const { return complex( re - c.re, im - c.im ); } complex complex::operator *( const complex &c ) const { return complex( re * c.re - im * c.im, re * c.im + im * c.re ); } complex complex::operator /( const complex & c ) const { double r1 = re; double i1 = im; double r2 = c.re; double i2 = c.im; return complex( ( r1 * r2 - i1 * i1 ) / ( r2 * r2 + i2 * i2 ), (-r1 * i2 + i1 * r2 ) / ( r2 * r2 + i2 * i2 ) ); } complex complex::operator ^( unsigned int n ) const { complex c( 1, 0 ); for ( unsigned int i = 1; i <= n; i++ ) c = c * *this; return c; } std::istream & operator >>( std::istream &is, complex &c ) { char ch; is >> ch; assert( ch == '(' ); is >> c.re; is >> ch; assert( ch == ',' ); is >> c.im; is >> ch; assert( ch == ')' ); return is; } std::ostream & operator <<( std::ostream &os, const complex &c ) { return os << '(' << c.re << ',' << c.im << ')'; } int main() { complex a(1, 1); complex b(1, 1); complex c(1, 1); complex x; std::cout << "Введите комплексное число в формате: (re,im) ->"; std::cin >> x; std::cout << "x = " << x << std::endl; std::cout << "x ^ 2 = " << ( x ^ 2 ) << std::endl; std::cout << "a * ( x ^ 2 ) = " << a * ( x ^ 2 ) << std::endl; std::cout << "b * x = " << b * x << std::endl; std::cout << "a * ( x ^ 2 ) + b * x = " << a * ( x ^ 2 ) + b * x << std::endl; std::cout << "Результат =" << a * ( x ^ 2 ) + b * x + c << std::endl; return 0; } Если ввести значение (10,10), то вывод будет выглядеть следующим образом: Введите комплексное число в формате: (re,im) ->(10,10) x = (10,10) x ^ 2 = (0,200) a * ( x ^ 2 ) = (-200,200) b * x = (0,20) a * ( x ^ 2 ) + b * x = (-200,220) Результат =(-199,221)

Ответ 2



Для таких простых классов с семантикой значения и малым размером лучше делать операторы свободными функциями (в силу семантики), и даже, возможно, с передачей по значению (в силу малости - 2 double скопировать против разыменований адресов (ссылка работает через адрес) - неизвестно, что выиграет... Как минимум, стоит выполнить несколько хронометражей): friend complex operator+(complex a, complex b) { return complex(a.re+b.re,a.im+b.im); } Update Провел хронометраж. VC++ 2015. На 32 разрядах константная ссылка быстрее примерно в 1.5 раза. На 64 разрядах одинаково. В принципе, более-менее понятно, почему. Ну, значит, нужно friend complex operator+(const complex& a, const complex& b) { return complex(a.re+b.re,a.im+b.im); }

пятница, 14 февраля 2020 г.

Как работает ссылка?

#cpp #массивы #ссылки


Друзья, помогите джуну в C++!
Когда пишем

char *str = "Hello";


То мы создаем указатель на первый символ в этой строке?
Тогда почему в cout << str; выводится вся строка, а не адрес первого символа?
    


Ответы

Ответ 1



Потому, что существует перегруженная версия оператора << для char * которая интерпретирует указатель на char как строку в стиле C. Если вы хотите вывести на печать адрес первого элемента массива, то нужно воспользоваться версией <<, которая не интерпретирует операнд как строку: std::cout << (void *) str << std::endl;

Ответ 2



Для char* возможно определить operator << так, чтобы в поток вводить все символы: ostream& operator <<(ostream& os, const char* p) { while (*p != '\0') os << *p++; return os; } в стандарте такое определение какраз и существует, но для других типов это невозможно, потому что в таком случаи нужно передать еше и размер, так как у других типов массив не ограничен специальным символом, но размер не можем передать, постольку поскольку этот оператор имеет 2 аргумента ("третий лишный"). Для эксперимента, вы можете определить свою версию, если уверены, что ваш указатель укажет на строку с более чем двух символов #include using namespace std; ostream& operator <<(ostream& os, const char* p) { while (*(p + 2) != '\0') os << *p++; return os; } int main() { const char* str= "hello"; //Да, именно const char*, а не char* cout << str; // выдаст "hel", т.е. на 2 символа меньше return 0; }

четверг, 13 февраля 2020 г.

Как вместо ссылки сделать активный текст?

#html #url #ссылки


Подскажите, пожалуйста, как составить программу или что нужно делать, чтобы вместо
длинной ссылки активным было одно слово. Например, вместо ссылки 
http://www.stroy-birzha.ru/request/?code=ffa8ce586819

Чтобы было слово 
Строй-Биржа.рф

Или вместо ссылки 
http://m.seaman-santehnika.ru/products/smesitel-seaman-eco-venice-ssn-1394p1/

Было активным слово смеситель с выдвижной лейкой? Спасибо всем, кто откликнется!    


Ответы

Ответ 1



Надо обернуть слово в тег (от англ. anchor, якорь): слово Например: Строй-Биржа.рф

Вернуть реализующийся интерфейс IEnumerable<T> через метод, C#

#c_sharp #ссылки #интерфейс #методы


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

К примеру, имеется код:

DataPerson dataPerson;
IEnumerable person = dataPerson.>Get(); 
// как я понял, метод Get() должен в person записать значение ссылочного типа 
// на реализующий интерфейс типа Person? Не представляю как реализовать это.
// В плане непонятны мне следующие вопросы: 

// 1. Где следует реализовать этот интерфейс? 

// 2. Не понимаю про двойное обобщение вида >,
// а именно каким образом это указать в методе Get()?

// 3. Не понимаю как вернуть интерфейс через метод.


Класс Person:

class Person 
{
    public string FirstName { get; set; }
    public string LastName { get; set; }

    public Person(string fName, string lName)
    {
        FirstName = fName;
        LastName = lName;
    }
}


Класс DataPerson:

class DataPerson
{
    // как я примерно представляю объявление функции Get()
    public IEnumerable Get() 
        where T : IEnumerable 
    {
        // как реализовать, к сожалению не представляю
    }   
}


Как собственно реализовать метод Get() в классе DataPerson?
    


Ответы

Ответ 1



Ваш метод скорее всего должен быть объявлен как class DataPerson { public IEnumerable Get() where T : new() { } } т.к. реально у него всего один тип-параметр - тип выбираемых объектов. и использоваться как IEnumerable person = dataPersonGet(); Реализовать его можно двумя способами: Действительно создать что-то, что реализует интерфейс IEnumerable. Например, массив или список. Заполнить его и вернуть из метода: class DataPerson { public IEnumerable Get() where T : new() { var query = buildSomeQueryFor(typeof(T)); // построить запрос для выбора объектов типа T DataRow[] dataRows = selectSomeRowsFromDB(query); // var result = new List(); foreach (var row in dataRows) { result.Add(MapDataRowTo(row)); } return result; } private MapDataRowTo(DataRow row) where T : new () { var newObject = new T(); // map values from row to newObject // return return newObject; } } Второй способо - использовать ключевое слово yield: class DataPerson { public IEnumerable Get() where T : new() { var query = buildSomeQueryFor(typeof(T)); // построить запрос для выбора объектов типа T DataRow[] dataRows = selectSomeRowsFromDB(query); // foreach (var row in dataRows) { yield return MapDataRowTo(row); } } private MapDataRowTo(DataRow row) where T : new () { var newObject = new T(); // map values from row to newObject // return return newObject; } } Использование yield завернет ваш метов в обертку-IEnumerable, которая будет возвращать элементы по мере обращения к ним из вызывающего кода. Т.е. выполнение метода Get будет происходить кусками, от одного вызова yield к следующему. В MSDN есть достаточно подробная документация по этой фиче языка.

Ответ 2



Как собственно реализовать метод Get() в классе DataPerson? class DataPerson { public IEnumerable Get() { return new List() { new Person(), new Person() }; } // или так public IEnumerable Get() { return new[] { new Person(), new Person() }; } }

воскресенье, 9 февраля 2020 г.

Каков механизм изменения значения переменных в цикле for Python

#python_3x #циклы #ссылки


Каким образом изменяется значение списков forenames и surnames, если мы явно не меняем
их значения? Это связано с names, какую роль это играет во внешнем цикле и как добавляя
в него элемент, мы меняем значения списков forenames и surnames? 

def get_forenames_and_surnames():

    forenames = []  
    surnames = [] 

    for names, filename in ((forenames, "forenames.txt"),
                                 (surnames, "surnames.txt")):

        for name in open(filename, encoding='utf8'):
            names.append(name.rstrip())
    return forenames, surnames

    


Ответы

Ответ 1



Для начала пример: a = [1, 2, 3] b = a a.append(4) print(a) print(b) Output [1, 2, 3, 4] [1, 2, 3, 4] Вопрос: почему поменялись оба списка, когда меняли только список a? Ответ: В Python переменные являются ссылками (идентификаторами) на хранящиеся в памяти объекты, поэтому: a = [1, 2, 3] означает - создай объект типа list, заполни его значениями 1, 2, 3 и присвой ссылку на этот объект переменной a. b = a - присвой переменной b ссылку, которая хранится в переменной a (которая в свою очередь указывает на объект). Теперь две переменных указывают на один и тот же объект. a.append(4) - найди объект по ссылке в переменной a и добавь в него элемент 4. Помним, что объект у нас один, на него указывают a и b. Если сделаем b.append(4), результат будет такой же. Упрощённая схема связи переменных и объектов: Теперь разберём ваш код: # Вспомогательная функция для печати идентификатора объекта def print_object_id(var_name, obj): # Функция id возвращает идентификатор объекта. obj_id = id(obj) print('{:X} - "{:s}" id'.format(obj_id, var_name)) def get_forenames_and_surnames(): print("===Назначение переменных===") forenames = [] # Печатаем, какой идентификатор присвоился переменной forenames print_object_id("forenames", forenames) surnames = [] # Печатаем, какой идентификатор присвоился переменной surnames print_object_id("surnames", surnames) print("\n===Внешний цикл for===") for names, filename in ((forenames, "forenames.txt"), (surnames, "surnames.txt")): ### На каждой итерации переменной "names" присваивается новый идентификатор: # В первой итерации это идентификатор переменной forenames # Вo второй итерации это идентификатор переменной surnames print_object_id("names", names) for name in open(filename, encoding='utf8'): # По идентификатору хранящемся в "names", Python ищет у # себя объект (в нашем случае это объект типа List) # и вызывает у этого объекта метод "append". # В итоге, новой элемент добавляется к списку. names.append(name.rstrip()) return forenames, surnames get_forenames_and_surnames() Output ===Назначение переменных=== 7F552E85B388 - "forenames" id 7F552E85B3C8 - "surnames" id ===Внешний цикл for=== 7F552E85B388 - "names" id ### Тот же самый, что и у forenames 7F552E85B3C8 - "names" id ### Тот же самый, что и у surnames

Ответ 2



Наиболее сложной является команда for names, filename in ((forenames, "forenames.txt"), (surnames, "surnames.txt")): То, что за in, есть кортеж из 2 элементов: (forenames, "forenames.txt") (первый элемент) (surnames, "surnames.txt") (второй элемент) Значит, тело цикла будет повторяться только дважды: первый раз для names == forenames и filename == "forenames.txt" второй раз для names == surnames и filename == "surnames.txt" В каждой из этих итераций выполнится тело цикла: for name in open(filename, encoding='utf8'): names.append(name.rstrip()) Значит, в первой итерации откроется файл forenames.txt и строки из него добавятся к списку forenames, в другой то же самое с файлом surnames.txt и списком surnames.

среда, 5 февраля 2020 г.

Изменить объект, на который указывает ссылка

#cpp #ссылки


У Скотта Мейерса написано:


  Если да, это подрывает основы, потому что C++ не позволяет изменить объект, на
который указывает ссылка.


Вопрос: почему приведенный далее код работает в VS2017?

int main()
{
    int i = 10;
    int& r = i;

    r++;

    int j = 20;
    r = j;

    i = 5;

    int& r2 = r;

    return 0;
}

    


Ответы

Ответ 1



Судя по всему, вы натолкнулись на этот текст тут. Но там речь идет о совсем другом - присваивании двух переменных, которые являются объектами класса, в которых имеется член-ссылка. И вопрос о том, что при этом должно происходить со ссылкой. В контексте имеется в виду не то, что нельзя изменять сам объект через ссылку, а то, что ссылка не может по ходу дела вдруг начать указывать на другой объект. С++ не позволяет никак менять значение самой ссылки, а не объекта, на который он указывает. Ссылка, в отличие от указателя, не может сначала указывать на объект a, а потом вдруг на объект b. Только на один и тот же объект - тот, которым она инициализирована - все время своего существования. Так понятнее? Да, фраза переведена несколько двусмысленно, не спорю...

Ответ 2



Вероятно, Вы не так поняли, что имел ввиду Мейерс. Изменение объектов, на которые указывают ссылки -- вполне возможное действие. Например, когда мы передаем объект в функцию по ссылке. В вашем коде когда вы оперируете с r, вы на самом деле оперируете с объектом, находящимся по адресу, который принадлежит i, и изменяете значение переменной i. Т.о. int & r = i; является "псевдонимом" для переменной i, как когда мы передаем объект в функцию по адресу (по ссылке, например).

воскресенье, 2 февраля 2020 г.

Возврат по ссылке/значению примитивных типов

#cpp #ссылки #перегрузка_операторов


В этом вопросе сказано, что примитивные типы лучше передавать в функцию по значению.
Насколько я понимаю, то же верно и для возврата из функции. Тогда почему операторы
индексации (например, вектора) возвращают исключительно ссылки, а не специализированны
под возврат по значению для фундаментальных типов? Тем более так наверняка будет использована
move семантика.

upd то, что я хочу сделать:

#include 

template
struct A
{
    T x;

    T& operator[](int) {return x;}
    const T& operator[](int) const {return x;}
};
template<>
struct A
{
    int x;

    int& operator[](int) {return x;}
    int operator[](int) const {return x;}
};


int main()
{
    const A a{};
    A b{};
    //a[0] = 3;
    b[0] = 3;
    std::cout << a[0] << ' ' << b[0];
}

    


Ответы

Ответ 1



Если специализировать для всех фундаментальных типов каклй-нибудь стандартный контейнер, который обладает множеством различных методов, то это будет очень не эффективно. Поэтому оператор индексирования в любом случае возвращает ссылку на объект. Передача по ссылке не является ресурсо-затратной операцией. Объекты при этом не создаются и не копируются. Более того для фундаментальных типов конструктор перемещения, как таковой, не существует. Поэтому и нет никакой необходимости специально перегружать стандартные контейнеры для фундаментальных типов, которых немало. К тому же получится неоднозначность с определением перегруженного оператора индексации, так как встроенный оператор возвращает всегда lvalue. Есть еще один нюанс. Даже если вы не собираетесь присваивать возвращаемому объекту новое значение, тем не менее у вас может быть необходимость хранить указатели на эти объекты. Например, у вас может возникнуть необходимость создать объект типа std::reference_wrapper допустим с помощью функции std::cref, который будет указывать на исходный объект в контейнере. Если не передавать ссылку на объект из контейнера, то вы не сможете воспользоваться такой возможностью. Рассмотрите следующий пример #include #include #include int main() { const std::vector v = { 3, 2, 4, 5, 1 }; std::vector> rv = { std::cref(v[0]), std::cref(v[1]), std::cref(v[2]), std::cref(v[3]), std::cref(v[4]) }; std::sort(rv.begin(), rv.end(), [](auto &a, auto &b) { return a.get() < b.get(); }); for (int x : v) std::cout << x << ' '; std::cout << std::endl; for (const auto &r : rv) std::cout << r.get() << ' '; std::cout << std::endl; } Вывод программы на консоль 3 2 4 5 1 1 2 3 4 5 В этой программе объявлен константный вектор. Однако, так как оператор operator [] возвращает ссылку на исходный объект, то можно связать его с объектом std::reference_wrapper, и тем самым представлять элементы исходного вектора упорядочными по различным критериям. То есть неважно, является ли объект константным или нет, тем не менее нередко необходимо получить на него ссылку, чтобы отслеживать его состояние. Константные объекты могут помимо прочего быть еще и volatile объектами или иметь члены данных с модификатором mutable. Если же возвращать объект по значению из оператора, то связь с исходным объектом будет утеряна, и вы будете иметь дело с копией.

Ответ 2



Думаю, это то, что требуется. template struct A { using ConstIndexRetType = typename std::conditional::value, T, const T&>::type; T x; T& operator[](int) { return x; } ConstIndexRetType operator[](int) const { return x; } }; int main(int argc, char **argv) { struct C {}; const A a; const A b; static_assert(std::is_same::value, "?"); static_assert(std::is_same::value, "?"); }

Ответ 3



А давайте глянем на практике... Так сказать, найдите три отличия: https://godbolt.org/g/s95gJt Вот это void f(const A& a, A& b) { std::cout << a[0]; std::cout << b[0]; } компилируется в push rbx mov rbx, rsi mov esi, DWORD PTR [rdi] mov edi, OFFSET FLAT:std::cout call std::basic_ostream >::operator<<(int) mov esi, DWORD PTR [rbx] mov edi, OFFSET FLAT:std::cout pop rbx jmp std::basic_ostream >::operator<<(int) Как видите, разницы никакой.

суббота, 1 февраля 2020 г.

Почему вызывается copy вместо move?

#cpp #ссылки #copy #move


Есть класс

class ArgClass {
public:
    ArgClass();
    ArgClass( const ArgClass& o );
    ArgClass& operator=( const ArgClass& o );
    ArgClass( ArgClass&& o );
    ArgClass& operator=( ArgClass&& o );
    ~ArgClass();
};


Шаблонная функция:

template< typename Type >
void wrapper( Type&& param )
{
    ArgClass tmp;
    tmp = param;
}


Почему данный код:

int main()
{
    ArgClass ac;
    wrapper( ArgClass() );
}


вызовет конструктор копирования в строке "tmp = param". Параметр функции wrapper
имеет тип ArgClass&&, и должен вызываться move конструктор копирования.
    


Ответы

Ответ 1



Потому что param внутри wrapper является локальной переменной, а значит, имеет имя, адрес... - словом, является lvalue. Чтобы он рассматривался при передаче в функцию rvalue как rvalue, нужно использовать прямую передачу (perfect forwarding) forward: tmp = std::forward(param);

Ответ 2



https://habr.com/post/226229/ В строке tmp = param вызовется оператор присваивания, почему конструктор копии? При явном вызове tmp = param; будет вызван оператор lvalue =, потому что он существует и определен, это гарантирует сохранность переданного объекта вне зависимости от того, как именно он принимается в функцию, как lvalue или rvalue. можно его удалить ArgClass& operator=( const ArgClass& o ) = delete; и убедиться, что компилятор пытается вызвать lvalue= class ArgClass { public: ArgClass() { std::cout << "base cons" << std::endl; }; ArgClass( const ArgClass& o ) { std::cout << "lv cons" << std::endl; }; ; ArgClass& operator=( const ArgClass& o ) { std::cout << "lv assign" << std::endl; return *this;};; ArgClass( ArgClass&& o ) { std::cout << "rv cons" << std::endl; };; ArgClass& operator=( ArgClass&& o ) { std::cout << "rv ass" << std::endl; return *this;};; ~ArgClass() { std::cout << "destr" << std::endl; };; }; template< typename Type > void wrapper( ArgClass&& param ) { ArgClass tmp; tmp = (param); } int main() { wrapper( ArgClass() ); system("pause"); } base cons base cons lv assign destr destr При явном вызове перемещения, будет вызван соответственно оператор rvalue= template< typename Type > void wrapper( ArgClass&& param ) { ArgClass tmp; tmp = std::move(param); } base cons base cons rv ass destr destr Для конструктора копии: template< typename Type > void wrapper( ArgClass&& param ) { ArgClass tmp(param); } base cons lv cons destr destr И перемещения: template< typename Type > void wrapper( ArgClass&& param ) { ArgClass tmp(std::move(param)); } base cons rv cons destr destr Такое поведение возможно обусловлено тем, что мув разрушает объект и именно явный вызов мув говорит о том, что объект будет утерян, в то время как обычные вызовы гарантируют сохранность объекта P.S. мувнуть объект можно и по обычной ссылке template< typename Type > void wrapper( ArgClass& param ) { ArgClass tmp(std::move(param)); } base cons rv cons destr destr

Ответ 3



Во-первых, о каком конструкторе вообще вы ведете речь, если у вас в коде вызывается оператор присваивания? Во-вторых, не вдаваясь в формальности: для именованного объекта никогда само по себе не будет выполняться move. param - это имя. Значит какой-то move исключено, пока вы явно не запросите его через std::move или std::forward. В данном контексте язык интересует не тот факт, что param имеет тип ArgClass&&, а тот факт, что выражение param является lvalue. Для lvalue будет вызван копирующий оператор присваивания, а не перемещающий.

среда, 29 января 2020 г.

Переопределение ссылок

#cpp #ссылки


В учебнике написано, что любая ссылка должна быть определена в момент объявления,
и определена объектом, а не литералом. И её нельзя перевязать на другой объект. Из
любопытства сделал следующее: 

{
int val = 20,val2 = 2;
int &refVal = val;
refVal = val2;
cout<


Ответы

Ответ 1



Дело в том, что ссылка это просто синтаксический сахар над указателями, поэтому, если переписать Ваш код через них, должно быть понятно, что происходит: { int val = 20,val2 = 2; int*const refVal = &val; *refVal = val2; cout<<*refVal<

Ответ 2



Ссылка это второе имя (псевдоним) переменной. Именно потому она обязательно должна быть определена: вторым именем можно быть только если есть первое. Учитывая что в с++ любой тип данных - класс, и любая переменная объект - ваш код не противоречит словам автора. Под литералом видимо подразумевалось строковая константа. (например "myLit")

вторник, 28 января 2020 г.

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

#java #ссылки #объекты #теория #параметры


Мой педагог задал мне задание ответить на ряд вопросов. И два их них меня сильно
смущают. 


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


Не могу понять смысл вопроса(((


  
  Как в Java передаются параметры в методы?
  


Я гуглил этот вопрос и понял что по всегда по значению но что точно это значит не понял.

Не могли бы вы мне помочь разобраться в этой ситуации.
    


Ответы

Ответ 1



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

Ответ 2



Как намекнул Vlad from Moscow в комментарии к вопросу, вопрос довольно странный, и на мой взгляд он не вполне корректен -- либо ваш преподаватель не очень хорошо умеет выражать свои мысли, либо вы как-то слегка исказили его вопрос. Дело в том, что в методе менять ссылку вообще-то можно. Строго говоря, в Jave обычно говорят не о ссылках на объект, а о переменных (полях, аргументах) объектного типа. Но фактически значением таких переменных являются ссылки на объекты. Методы в качестве своих аргументов получают копии этих ссылок и могут делать с ними что хотят (т. е. присваивать им значения ссылок на другие объекты или null - "пустую ссылку"), что никак не отражается на значениях переменных, которые были переданы методу при его вызове. Так что вот такой код public class TestQQ { static void qq(String s) { System.out.println(s); s = "Что-то новое..."; // Это ссылка или не ссылка меняется ? System.out.println(s); } public static void main(String[] args) { String string = "Нельзя менять ссылки?"; qq(string); System.out.println(string); } } Создает новый объект класса String со значением "Нельзя менять ссылки?"; Присваивает ссылку на него переменной string; Вызывает метод qq() и передает ему копию переменной string (т. е. создает вторую ссылку на ту же строку "Нельзя менять ссылки?") Метод qq() выводит на консоль содержимое того объекта, ссылка на который содержится в его параметре - т. е. то же самое "нельзя?"; Метод qq() своему параметру s присваивает новое значение - ссылку на новую строку "Что-то новое...", -- вторая ссылка на "нельзя" исчезает, но исходная переменная string в главной программе при этом никак не меняется и по-прежнему содержит ссылку на то же самое изначальное "нельзя?"; Метод qq() печатает новую строку ("Что-то новое...") и возвращает управление в главную программу (метод main); Метод main() печатает содержимое объекта, который содержится (т. е. ссылка на который содержится) в переменной string - это по-прежнему изначальная строка "Нельзя менять ссылки?" Вывод будет такой:Нельзя менять ссылки? Что-то новое Нельзя менять ссылки? Но при этом надо отчетливо понимать, что хотя метод не может изменить значение объектной переменной, переданной ему в качестве параметра (т. е. ссылку на объект), он за милую душу может изменить сам объект! Например, в такой программе import java.util.ArrayList; public class TestQQQ { static void qqq(ArrayList list) { list.add("можно."); } public static void main(String[] args) { ArrayList strings = new ArrayList(); strings.add("А объекты менять...?"); qqq(strings); System.out.println(strings); } } метод qqq() добавляет в список, ссылку на который он получил, новую строку "можно.", и в результате мы получаем на выходе вот что: [А объекты менять...?, можно.]

Ответ 3



Почему нельзя изменить ссылку на объект в методе? При передаче объекта в метод происходит просто копирование ссылки на него (Никаких копий объекта не создаётся при передачи!). И уже с помощью неё вы общаетесь с исходным объектом. Отсюда должно быть понятно, почему при перезаписывании в эту ссылку нового объекта старый не меняется. Простой пример для разъяснения: List list1 = new ArrayList<>(); //Создаём объект и кладём в ссылку List list2 = list1; //Просто копируем ссылку list2.add(new Object); //Через копию ссылки мы меняем исходный объект в list1 list2 = new ArrayList<>(); //Хоть мы и переписали в ссылку новый объект, //исходный никуда не делся, он всё также лежит в list1. Как в Java передаются параметры в методы? Примитивы по значению, объекты - как я описал выше (не знаю как правильно называется этот способ). Update: спасибо @Roman, правильное название способа передачи объектов в метод - вызов по соиспользованию (call by sharing)

воскресенье, 5 января 2020 г.

В чём смысл std::forward при передаче параметров?

#cpp #шаблоны_с++ #ссылки #параметры


В чём разница между следующими двумя способами передачи параметров?

template  smth(Args &&...args) : base(args...) {}




template  smth(Args &&...args) : base(std::forward(args)...) {}

    


Ответы

Ответ 1



Предположим, что конструктор base принимает move-only параметр по значению: struct base { base(std::unique_ptr value); }; В таком случае первый вариант не скомпилируется: template smth(Args &&...args) : base(args...) {} попытка сделать копию ^^^^ Тут можно написать std::move и это будет работать: template smth(Args &&...args) : base(std::move(args)...) {} Однако в случае если у base два конструктора, один из которых перемещает значение, а другой принимает ссылку: struct base { base(std::unique_ptr&& value); base(std::unique_ptr& value); }; И если мы хотим чтобы smth передавал в base именно те типы аргументов, с которыми его вызвали, то std::move нам не подойдет - он всегда будет превращать аргумент в r-value ссылку. Для этого есть std::forward, который сохраняет оригинальный тип аргумента: struct smth : base { template smth(Args &&...args) : base(std::forward(args)...) {} }; std::unique_ptr p; smth s1(p); // OK, будет вызван base(std::unique_ptr&); smth s2(st::move(p)); // OK, будет вызван base(std::unique_ptr&&); Примечание: std::forward(t) работает только с "универсальнами ссылками", т.е. когда T - это параметр шаблона функции (конструктора), а t - аргумент этой функции с типом T&& (это специальное исключение в языке, такой тип T&& не является r-value ссылкой, а заставляет T повторять тип того, что передали в функцию).

вторник, 31 декабря 2019 г.

Смысл ссылок в методах функций C++

#cpp #ссылки #перегрузка_операторов #оператор_присваивания


Всем привет, я начинающий в C++, возник вопрос касательно смысла ссылок в методе функции.
Есть класс MyString, в нём два private поля: st - char массив и size - длина массива.
Вот я делаю перегрузку оператора =

MyString & operator =(MyString & tmp)


Зачем ставится ссылка в аргументе я знаю, это указание системе, что нужно не копировать
объект, а взять его адрес. Но что значит & перед operator? В каких случаях нужно использовать?
    


Ответы

Ответ 1



Ссылками могут быть не только параметры, но и возвращаемые значения. Во-первых, такое определение перегруженного оператора присваивания согласуется с требованием для встроенных операторов присваивания согласно стандарту C++ (5.18 Assignment and compound assignment operators) 1 The assignment operator (=) and the compound assignment operators all group right-to-left. All require a modifiable lvalue as their left operand and return an lvalue referring to the left operand... Во-вторых, Это позволяет избежать копирования исходного объекта в качестве возвращаемого значения, для которого было выполнено присвоение. То есть оператор возвращает ссылку на свой левый операнд (MyString &). В-третьих, это позволяет записывать такие конструкции как, например, MyString a, b, c; (a = b ) += c; Имейте в виду, что объявленный таким образом оператор присваивания MyString & operator =(MyString & tmp); не сможет иметь дело с константными или временными объектами в качестве правого операнда, так как параметр оператора объявлен как не константная ссылка, которую нельзя привязывать к временным объектам. Более универсальный подход - это объявить оператор как MyString & operator =(const MyString & tmp);

Ответ 2



& относится к типу возвращаемого результата. В данном случае означает, что возвращается ссылка на MyString. Оператор присвоения в С/С++ должен возвращать результат присвоения, который равен значению переменой слева от = после присвоения. Можно возвращать и копию результата, то есть без ссылки, но это может быть неэффективнее, если результат содержит много данных. Важно отметить, что если возвращаешь ссылку, то эта ссылка должна быть на что-то, что будет продолжать жить после выхода из кода оператора. (Обычно, конечно, операторы присвоения возвращают ссылку на *this, то есть на сам объект от имени которого вызывается оператор - тот объект который слева от = ). Кстати, некоторые рекомендуют возвращать константную ссылку: const MyString& operator=(const MyString& tmp); чтобы нельзя было писать такие вещи: (a=b)=c; Потому что после выполнения такого выражения, b будет не равно a и c, если изначально b было не равно c. Считается что это выглядит не интуитивно понятно. Но это необязательно. Например многие популярные библиотеки классов так не делают.

Шифрование ссылок для скачивания

#файлы #php #ссылки #шифрование


Вопрос довольно популярный, объясните пожалуйста каким образом можно наиболее адекватно
шифровать ссылки для скачивания. То есть пользователь вошел -> для него генерируется
ссылка, допустим на zip архив, ну и удаляется после скачки. Ни когда таким не занимался,
стало интересно.    


Ответы

Ответ 1



Можно пользоваться средствами веб сервера: модуль SecureLink для nginx (вовсю использую); и, кажется, mod-auth-token для Apache. Upd. Можно написать и свою реализацию на том же php, но есть очевидный минус: перформанс. Чтобы не показывать истинный адрес файла, придётся пропускать его контент сквозь работающий php скрипт в течение всего времени скачивания. Т.е. в памяти будет висеть по тяжеленному процессу php на каждую сессию скачивания. А веб сервер делает то же самое, легко и изящно. А, ещё можно создавать временную копию ценного файла, или, лучше, линк на него, со странным именем-абракадаброй, и стирать через опр. время.

Ответ 2



Вот тут человек приводит свой готовый класс для таких целей: Временная ссылка для скачивания файла.

Ответ 3



С помощью php придумываем ссылку и с помощью mod_rewrite подсовываем файл. Например такой .htaccess где index.php обрабатывает все запросы: Options +SymLinksIfOwnerMatch RewriteEngine On RewriteBase / RewriteRule .* index.php [L]

Ответ 4



Я решил так задачу. Зделал файл скачивания назовем его download.php При заходе участника (За регестрированного пользователя) на сайт даем ему случайно сгенерированный ключ, который активен пока пользователь на сайте ?key=KJSJH#&&#EGJHJ к нему прикручиваем имя файла и директорию пользователя в которой находится файл. Отдаем все это через Header() application/octet-stream Ключ можно проверять через сессию например $_SESSION['confirm_download_key']; незнаю насколько эффективно, но пока работает.

понедельник, 30 декабря 2019 г.

Передача параметра по ссылке с++

#cpp #функции #ссылки #параметры


Как передать функции параметр по ссылке со значением по умолчанию?

void func(int &i)

void func2(set &s)


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


Ответы

Ответ 1



Если вы хотите использовать аргумент по умолчанию по ссылке, то этот аргумент должен быть не временным объектом и уже где-то быть определенным на момент вызова функции. Передача аргумента по неконстантной ссылке предполагает, что этот исходный аргумент будет в функции изменен. Вы не можете передавать по неконстантной ссылке временные объекты. Поэтому, исходя из условий, указанных в вашем вопросе, скорей всего у вас должны быть две перегруженные функции. Одна функция принимает аргумент по lvalue-ссылке с конкретно заданным объектом. Вторая функция оибл принимает временный объект по rvalue-сслке, оибо вместо него использует значение по умолчанию. Например, #include void func( int &i ) { ++i; std::cout << "Inside func( int & ) i = " << i << std::endl; } void func( int &&i = int() ) { ++i; std::cout << "Insde func( int && ) i = " << i << std::endl; } int main() { int i = 0; std::cout << "Before func( i ) i = " << i << std::endl; func( i ); std::cout << "After func( i ) i = " << i << std::endl; std::cout << std::endl; i = 0; std::cout << "Before func() i = " << i << std::endl; func(); std::cout << "After func() i = " << i << std::endl; return 0; } Вывод программы на консоль: Before func( i ) i = 0 Inside func( int & ) i = 1 After func( i ) i = 1 Before func() i = 0 Insde func( int && ) i = 1 After func() i = 0 В этой программе, когда в качестве аргумента передается lvalue, то используется первая функция void func( int &i ) { ++i; std::cout << "Inside func( int & ) i = " << i << std::endl; } и она меняет в своем теле исходный аргумент. Когда же аргумент не указывается, или когда указывается rvalue, то есть некоторый временный объект, как, например, func(); // используется аргумент по умолчанию int(), который равен 0 или func( 10 ); // используется временный объект - целочисленный литерал - 10 то используется вторая функция void func( int &&i = int() ) { ++i; std::cout << "Insde func( int && ) i = " << i << std::endl; } Если же вы не собираетесь изменять исходный аргумент, то достаточно объявить одну функцию с параметром в виду константной ссылки. Например, void func( const int &i = int() );

Ответ 2



Ну, первый случай несложен - если вы хотите иметь возможность использовать значение по умолчанию, то используйте const int& - void f(const int& i = 5) { cout << i << endl; } Без const, как вы понимаете, вам не удастся передать значение - ведь что тогда вы собираетесь менять, передавая число? :) А вот так - можно: int j = 5; void f(int& i = j) { cout << i << endl; } int main(int argc, const char * argv[]) { int q = 4; f(); f(q); } Соответственно, то же и с set& - как вы себе представляете для s вызывался конструктор, чтобы использовать множество set в теле функции Т.е. вы хотите создавать новый set? Тогда где он должен быть создан? Как локальная переменная, как глобальная, как переменная в вызывающей функции? Так что по умолчанию для ссылки надо указывать имеющийся объект. Другое просто не имеет никакого смысла. В конце концов, ссылка - это просто адрес, ждя которого гарантируется наличие корректного объекта. "По-моему, так" (с) Пух

Ответ 3



Если вам нужна именно неконстантная ссылка, то сделать в такой ситуации можно что-то вроде void func2(set &s = some_function()) где some_function это set &some_function() { ... } некая функция, которая отвечает за создание этих экземпляров std::set в некоем хранилище. Отдельно придется подумать об обеспечении некоей политики их последующего уничтожения, когда они станут ненужными. Как это будет делаться - вопрос отдельный и элегантных решений тут навскидку не видно. Оптимальное решение может зависеть от конкретной специфики вашей задачи, но сама идея неявного создания (потенциально долгоживущих) объектов через параметр по-умолчанию - сомнительна.

воскресенье, 29 декабря 2019 г.

Ссылки и указатели

#g++ #указатели #ссылки #cpp


Я думаю, все знают про ссылки и указатели. 
В общем, когда я попытался вывести на экран адрес переменной (cout << &a << endl;),
я получил шестнадцатеричное число. Что оно означает и имеет ли смысл для программиста?    


Ответы

Ответ 1



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

Ответ 2



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

Ответ 3



Будет иметь смысл при передаче аргументов посредством указателей, вместо целых больших данных. При инициализации указателя в его память записывается посредством & адрес объекта. И эта переменная при передаче в функцию как показывает ей где хранятся эти данные в памяти программы.

суббота, 28 декабря 2019 г.

Передача в функцию указателя или ссылки [дубликат]

#cpp #указатели #ссылки


        
             
                
                    
                        
                            This question already has answers here:
                            
                        
                    
                
                        
                            Чем отличаются ссылки от указателей в С++
                                
                                    (2 ответа)
                                
                        
                                Закрыт 3 года назад.
            
                    
В чем будет разница, если в функцию передать указатель и ссылку?
например

#include   
void func(int *a)
{
  *a = 5
}
int main()
{
  int a;
  func(a);
  std::cout << a;
}


и

#include   
void func(int &a)
{
  a = 5
}
int main()
{
  int a;
  func(a);
 std::cout << a;
}


Программы выведут одно и то же?
Ведь и так, и так мы обращаемся к существующему элементу, а не создаем новый.
    


Ответы

Ответ 1



В принципе, технически разница между указателем и ссылкой лишь в том, что саму ссылку нельзя изменить (а указатель можно). [Вот тут бóльший список отличий.] Кроме этого, разница ещё синтаксическая: с ссылкой вы обращаетесь как будто это переменная, а с указателем нужно его правильно получать/разыменовывать: void func(int *a) { *a = 5; } int main() { int a; func(&a); cout << a; } Других технических отличий нет, и результат будет одинаковый. Но разница на самом деле не в синтаксисе, а в смысле, в семантике. Указатель может означать всё, что угодно. Он в C и C++ означает строку, ассив, адрес переменной, передачу переменной по ссылке и ещё кучу всяких вещей. А смысл ссылки ровно один — это как бы альтернативное имя (alias) существующей переменной. Поэтому для случаев наподобие того, который вы описали, в C++ уместно использовать ссылку, а не указатель. Хотя, как вы сами видите, с указателем тоже прекрасно работает. И ещё: в чистом C ссылок нет, так что у вас нет другого варианта кроме указателей.

Ответ 2



Ответы на ваш вопрос даны выше, разницы в поведении никакой, но хотел бы уточнить несколько нюансов. 1. Пока у вас простые типы разницы никакой (кроме описаной выше в других ответах, например про NULL значение переменной в случае указателя). Но как только дело дойдет до классов появится разница. В случае передачи в функцию указателя на класс то, в зависимости от реализации и решаемых задач, будет иметь место разыменовывание указателя. Сложность этой процедуры будет зависеть от размера класса. Это справделиво в случе разыменовывания всего класса и не касается доступа к отдельным его членам. Например: int c = b->d; //дешево, а A b = *A; //дороже 2. вы можете перегрузить оператор &, а * нет. 3. Указатели не могут принимать временные значения, в то время как const & могут. Например: void f(const &A);.... f(A()); //возможно 4. В целом работа с ссылками проще, поэтому ведут к меньшему числу ошибок Золотое правило с++ программиста: используй ссылки где можешь, а указатели где должен.

Ответ 3



Если вы исправите опечатки и включите заголовок #include void func(int *a) { *a = 5; ^^ ^ } int main() { int a; func(a); std::cout << a; ^^^^^ } и #include void func(int &a) { a = 5; ^ } int main() { int a; func(a); std::cout << a; ^^^^^ } То вывод будет идентичен. Когда используются указатели, то вы можете передать null-указатель. Например, void func(int *a) { if ( a ) *a = 5; } И функция может быть вызвана как int x; func( &x ); func( nullptr ); Когда же используются ссылки, то ссылка должна указывать на существующий объект.

пятница, 27 декабря 2019 г.

Переменные-ссылки в Python

#python #указатели #ссылки


Я так понял, что все переменные в Python - это ссылки, которые ссылаются на один
объект, а не на участок памяти как в С/C++. 

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

Например (условно),

a = 10
b = &a
print(a, b)  # 10 10
b = 20
print(a, b)  # 20 20
a = 15
print(a, b)  # 15 15


Мне это нужно, чтобы сокращать запись для доступа ко внутренним значениям вложенных
словарей:

username   = "test"
userdata   = [10, 2, 30, 15]
user       = {username:userdata}

serveraddr = "127.0.0.1"
server     = {serveraddr:user}

# хочется создать имена-сокращения для этих элементов    
joincount  = server[serveraddr][username][0]
leftcount  = server[serveraddr][username][1]
gamescount = server[serveraddr][username][2]
wincount   = server[serveraddr][username][3]

# пробуем изменить их значения
joincount  = 20
leftcount  = 9
gamescount = 10
wincount   = 3

# проверяем, что значения в словаре остались прежними
print(server[serveraddr][username][0], server[serveraddr][username][1],
      server[serveraddr][username][2], server[serveraddr][username][3])
# (10, 2, 30, 15)

# хотя значения переменных изменились
print joincount, leftcount, gamescount, wincount
# (20, 9, 10, 3)


В коде очень часто используются конструкции вида server[serveraddr][username][n],
большая их часть приходится на места где все индексы вычислены. Поэтому хотелось бы
вместо server[serveraddr][username][0] использовать joincount - это улучшило бы читаемость
кода.

Возможно ли это реализовать?
    


Ответы

Ответ 1



В Python есть изменяемые и неизменяемые типы. Изменяемые отличаются тем, что их содержимое можно сменить, не изменив ссылку на них. Неизменяемые объекты приходится пересоздавать, чтобы отразить изменения состояния. При этом все старые ссылки не видят это обновление, потому что указывают на старый объект. Поясню на практике. Списки, словари, множества - это изменяемые объекты: l1 = [1, 2, 3] l2 = l1 print(l1, l2, id(l1), id(l2)) # [1, 2, 3] [1, 2, 3] 139917408901064 139917408901064 l1[1] = 10 print(l1, l2, id(l1), id(l2)) # [1, 10, 3] [1, 10, 3] 139917408901064 139917408901064 Числа, строки, кортежи - это неизменяемые объекты: v1 = 1024 v2 = v1 print(v1, v2, id(v1), id(v2)) # 1024 1024 ...7040 ...7040 v1 = 2048 print(v1, v2, id(v1), id(v2)) # 2048 1024 ...5312 ...7040 t1 = (1, 2, 3) t2 = t1 print(t1, t2, id(t1), id(t2)) # (1, 2, 3) (1, 2, 3) ...6232 ...6232 # t1[1] = 10 # не сработает, так как кортежи неизменяемые t1 = (1, 10, 3) print(t1, t2, id(t1), id(t2)) # (1, 10, 3) (1, 2, 3) ...7240 ...6232 В своём коде вы используете изменяемые структуры данных, такие как словари и списки (user, userdata, server) и неизменяемые строки и числа. Таким образом, ничего не изменяя в структуре хранения информации, максимально можно сократить только доступ до списка чисел my_userdata = server[serveraddr][username] # изменение my_userdata влечёт изменение server[serveraddr][username], # так как это один и тот же объект Так как числа неизменяемые, то сохранение их в переменную и дальнейшее изменение этой переменной никак не сказываются на исходном числе (т.е. переменная joincount никогда не изменится при изменении server[serveraddr][username][0], и наоборот). изменение чисел в этом списке никак не отразится на переменных, хранящих эти значения Но если попробовать немного улучшить код, то стоит создать класс UserData, который будет хранить в себе нужные значения и позволять их изменять. Минимальный пример будет следующий: class UserData: def __init__(self, joincount, leftcount, gamescount, wincount): self.joincount = joincount self.leftcount = leftcount self.gamescount = gamescount self.wincount = wincount username = "test" userdata = UserData(10, 2, 30, 15) user = {username:userdata} После этого, если мы получаем откуда-то нашего пользователя, мы можем изменить нужные значения: user_data = server[serveraddr][username] user_data.joincount = 20 user_data.leftcount = 9 user_data.gamescount = 10 user_data.wincount = 3 print (server[serveraddr][username].joincount, server[serveraddr][username].leftcount, server[serveraddr][username].gamescount, server[serveraddr][username].wincount) print (user_data.joincount, user_data.leftcount, user_data.gamescount, user_data.wincount) Использование подобной структуры увеличит читаемость вашего кода, по сравнению с использованием индексов списка. Если есть доступ на изменение содержимого словаря, я бы рекомендовал вам вместо самописного класса использовать namedtuple из стандартного модуля collections, который позволяет создавать объект, по поведению схожий с кортежем, но предоставляющий доступ к полям, как у класса выше (но только на чтение): UserData = collections.namedtuple('UserData', ['joincount', 'leftcount', 'gamescount', 'wincount']) В этом случае придётся заменять старый объект UserData новым: old_userdata = server[serveraddr][username] new_userdata = UserData(joincount=20, leftcount=9, gamescount=10, wincount=3) server[serveraddr][username] = new_userdata print(new_userdata, old_userdata, server[serveraddr][username]) Получение поля на чтение будет выполняться так же, как и в примере выше: print(old_userdata.joincount, old_userdata.gamescount)

Ответ 2



Вам нужно прочитать и разобраться с mutable и immutable объектами в python. Да вы можете делать ссылку на вложенный список, и заполнять его через нее. Например: lst = [[]] sublst = lst[0] sublst.append(1) print(lst) sublst.append(2) print(lst) print(id(lst[0])) print(id(sublst)) sublst[0] = -1 print(lst) Вывод: [[1]] [[1, 2]] 139794716732424 139794716732424 [[-1, 2]] В данном случае sublst это ссылка на вложенный список в lst, вызов sublst.append равнозначен lst[0].append. Аналогично вы можете работать например со словарем.

четверг, 19 декабря 2019 г.

Как узнать размер массива переданного в функцию?

#cpp #c #массивы #указатели #ссылки


Необходимо определить размер массива, переданного в функцию. Пробовал вот так:

void foo(int* Array)
{
    const unsigned int SIZE = sizeof(Array)/sizeof(int);
}


но в SIZE сохраняется 1, независимо от размера массива. Можно, конечно, вместе с
массивом передать в функцию и его размер, но может существует более изящное решение?

P.S.: Кстати, заметил нечто странное. Запускал эту программу через Visual Studio
и Qt. В VS в SIZE сохраняется 1, а в Qt 2.
    


Ответы

Ответ 1



У вас параметр функции foo объявлен как указатель типа int * void foo(int* Array); ^^^^^^^^^^ Следовательно внутри функции выражение sizeof(Array)/sizeof(int) эквивалентно выражению sizeof(int *)/sizeof(int) Если, например, размер указателя, то есть типа int *, равен 8 байтам, а размер типа int равен 4 байтам, то в итоге вы получите 2. Если же при этом размер типа int равен также 8 байтам (64-битовая ОС), то вы получите в итоге 1. Но даже если вы объявите эту функцию как void foo(int Array[]); или даже так void foo(int Array[10]); все равно параметр функции неявно преобразуется в указатель на элемент массива. То есть эти два объявления функции объявляют одну и ту же функцию и эквивалетны следующему объявлению void foo(int* Array); Так что внутри функции вы снова будете иметь дело с указателем. Когда массив передается по значению, то вам следует также объявлять второй параметр, который задает размер массива. Или массив должен иметь некоторый граничный элемент с уникальным значением, по которому можно определить число актуальных элементов, как это имеет место, например, со строками, когда строки завершаются нулем, то есть символом '\0'. То есть в общем случае вам следует объявлять функцию как void foo(int* Array, size_t n); где n - это размер массива. Другой подход - это объявлять параметр как ссылку на массив. В этом случае длина массива будет известна внутри функции. Например void foo( int ( &Array )[10] ) { const size_t = sizeof( Array)/ sizeof( *Array ); } Недостаток этого объявления состоит в том, что эта функция может иметь дело только с массивами, заданного в ее параметре размера. Чтобы обойти это ограничение, вы можете объявить шаблонную функцию. Например, template void foo( int ( &Array )[N] ) { const size_t n = N; } В этом случае компилятор, используя шаблон, создаст столько функций, сколько массивов разной длины были использованы в качестве аргумента.

пятница, 13 декабря 2019 г.

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

#cpp #указатели #ссылки


C++:  

    void f(int *ptr)
    {
       *ptr;
    }

    void f(int &ptr)
    {
       ptr;
    }


ASM:

f(int*):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        nop
        pop     rbp
        ret
f(int&):
        push    rbp
        mov     rbp, rsp
        mov     QWORD PTR [rbp-8], rdi
        nop
        pop     rbp
        ret


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


Ответы

Ответ 1



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

Ответ 2



И ссылка и указатель - обеспечиваю програмисту доступ к обьекту используя адрес в памяти. Поэтому при одинаковых попытках обратится к обьекту или его полям - приведут к генерации одинакового кода. 1.Например void f(int *ptr){*ptr;} равнозначно void f(int &ptr){ptr;} 2.void f(int *ptr){ptr;} равнозначно void f(int &ptr){&ptr;} 3.void f(obj*p){p->toStr();}равнозначноvoid f(obj& p){p.toStr();} Запись разная - а значение - одинаковое. Есть правда некоторые особенности, но они не влияют на то какой код сгенерируется - если суть одинакова - сгенерируется одинаковый код: Указатель может быть пустым - ссылка не может быть пустой. при использовании new delete this более удобны переменные с использованием указателя (по ссылке можно - но гораздо сложнее выражения). через указатель, можно использовать обьект, как массив (void f(int *ptr){ptr[0];}), через ссылку нет. доступ к полям при указателе происходит посредством ->, а к полям ссылки через точку .. от указателя можно перейти к ссылке используя унарный оператор * и от ссылки можно перейти к указателю используя унарный оператор &. (не унарные опрераторы выполняют математические или логические действия).