3 / 3 / 0
Регистрация: 02.04.2018
Сообщений: 47
1

Дружественная статическая функция из другого класса

08.05.2019, 16:24. Показов 775. Ответов 11
Метки нет (Все метки)

у меня есть класс Info , объект которого имеет поля ключ и значение , оба типа std::string
допустим я запущу сначала команду set q 1 и в базу занесется ключ q со значением 1, а затем set q 3 и нужно, чтобы значение ключа q поменялось с 1 на 3, то есть мне нужно поменять значение поля value

Для этого я сделала функцию change в классе work,которая является дружественной к классу Info(ну в принципе и как функция класса Info та же ошибка). Проблема в том, что в строчке файла work.cpp a.value = b; появляется ошибка passing const String { ... здесь еще что то } as this argument discards qualifiers , красным выделена переменная b.Что не так? Вызов функции делаю по итератору: change( (*it).value, user_value ) в разделе SET функции work:arse
main.h

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <set>
#include <string>
#include <iterator>
#include "work.h"
#include "key-value.h"
 
using namespace std;
 
int main(){
    set <Info> DataBase;
 
 
    while (1){
        string str;
 
        getline(cin, str);
        if (str == "quit") break;
        
        work::parse(str, DataBase);     
        cout << endl;
    }
    return 0;
}
 
/*           Формулировка задания        
Написать СУБД (систему управления базой данных), хранящую данные в формате ключ-значение. 
Тип ключа - строка, значение может любого типа, который можно распечатать (перегружен оператор <<).
 
СУБД имеет следующий набор операций:
 
SET ключ значение 
если элемента с таким ключом нет - он добавляется
 
GET ключ
если элемент с таким ключом есть - выдается его значение, иначе - сообщение об отсутствии элемента с таким ключом
 
KEYS
распечатываются все ключи, хранящиеся в нашей БД
Работа с СУБД происходит в цикле, выход - по команде quit.
 
Хранение пар ключ-значение сделать на основе std::set. В классе, реализующем пару ключ-значение, 
должен быть перегружен оператор <. Он должен быть константным и иметь константный аргумент.*/
work.h

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#pragma once
#include <iostream>
#include <set>
#include <string>
#include <iterator>
 
using namespace std;
class Info;
 
class work{
 
    static int gap_jump(const string &str, int &i);
 
    static int to_gap(const string &str, int &i);
public:
    static int change(const Info &a, const string &b);
 
    static void parse(const string &str,set <Info> &db);
};
work.cpp

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
#include <set>
#include <string>
#include <iterator>
#include "work.h"
#include "key-value.h"
using namespace std;
class Info;
 
int work::gap_jump(const string &str, int &i){
    while (str[i] == ' ') i++;
    if (str[i] == '\0') return 1;
    return 0;
}
 
int work::to_gap(const string &str, int &i){
    while(str[i] != ' ' && str[i] != '\0') i++;
    if (str[i] == '\0') return 1;
    return 0;
}
int work::change(const Info &a, const string &b){
    a.value = b;
    return 0;
}
void work::parse(const string &str,set <Info> &db){
    int i = 0;
 
    if (str.empty() || gap_jump(str,i)){ cout << "ERROR: No command!\n"; return; };
 
    int k = i;
    to_gap(str,i);
 
    if (str.substr(k, i - k) == "KEYS" || str.substr(k,i - k) == "keys"){
        /*****************************************
                       KEYS
        *****************************************/
        // ...
    }else if (str.substr(k,i - k) == "SET" || str.substr(k,i - k) == "set"){
        /*****************************************
                       SET
        *****************************************/
        if (gap_jump(str,i)){ cout << "ERROR: No arguments for command***\n"; return; };
 
        k = i;
        to_gap(str,i);
        
        const string user_key = str.substr(k, i - k);
 
        if (gap_jump(str,i)){ cout << "ERROR: No arguments for command\n"; return; };
 
        k = i;
        int search_flag = 0;
        to_gap(str,i);
        const string user_value = str.substr(k, i - k);
 
        if (!gap_jump(str,i)){ cout << "ERROR: Too much arguments!\n"; return; };
        
        for (set <Info> :: iterator it = db.begin(); it != db.end(); ++it){
            if ((*it).get_key() == user_key){ 
                cout << "The value of this key was replaced in data base\n"; 
                search_flag = 1; 
                change((*it), user_value);
            }
        }
        if (!search_flag){
            db.insert(Info(user_key, user_value));
            cout << "Key has been added to data base\n";
        }
 
    }else if (str.substr(k,i - k) == "GET" || str.substr(k,i - k) == "get"){
        /*****************************************
                       GET
        *****************************************/
        if (gap_jump(str,i)){ cout << "ERROR: No arguments for command\n"; return; };
                // ...
    }else{
        cout << "ERROR: Wrong command!\n";
        return;
    }
}

Info.h
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <set>
#include <string>
#include <iterator>
#include "work.h"
using namespace std;
 
class Info{
    string key;
    string value;
public:
    Info( const string keyy,const string valuee);
    Info(const Info &a);
    const string& get_key() const;
 
    const string& get_value() const;
    friend int work::change(const Info &a,const string &b);
    friend ostream &operator << (ostream &out, const Info &m);
};
Info.cpp
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <iostream>
#include <set>
#include <string>
#include <iterator>
#include "key-value.h"
 
 
using namespace std;
 
Info::Info( const string keyy,const string valuee){
    copy(keyy.begin(), keyy.end(), back_inserter(key));
    copy(valuee.begin(), valuee.end(), back_inserter(value));
}
Info::Info(const Info &a){
    copy(a.key.begin(), a.key.end(), back_inserter(key));
    copy(a.value.begin(), a.value.end(), back_inserter(value));
}
const string& Info::get_key() const{
    return key;
}
 
const string& Info::get_value() const{
    return value;
}
ostream& operator <<(ostream &out, const Info &m){
    out << m.key << " - " << m.value;
    return out;
}
__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь
0
Programming
Эксперт
94731 / 64177 / 26122
Регистрация: 12.04.2006
Сообщений: 116,782
08.05.2019, 16:24
Ответы с готовыми решениями:

Дружественная функция класса
Проблема в том что дружественная функция класа механик в класе car (friend void update_status) не...

Шаблон класса и дружественная функция
Доброго времени суток. Столкнулся с непониманием одного процесса. Задача звучит следующим образом:...

Дружественная функция и приватные члены класса
Здравствуйте, уважаемые форумчане! Подскажите, пожалуйста, можно ли как-то в коде ниже сделать...

Дружественная шаблонная функция шаблонного класса
Не компилируется. Подскажите правильный синтаксис. template&lt;typename T&gt; class A;...

11
5232 / 2886 / 1208
Регистрация: 07.02.2019
Сообщений: 7,283
08.05.2019, 16:37 2
hardequation,
Во первых, для хранения пар ключ - значение используют map.
Во вторых, что бы поместить объект в set, для него должен быть как минимум определен оператор сравнения < .
В третьих, нельзя изменять объекты, хранящиеся в set, хотя можно хранить константный указатель, и изменять объект, на который он указывает.
0
3 / 3 / 0
Регистрация: 02.04.2018
Сообщений: 47
08.05.2019, 17:42  [ТС] 3
Нужно было именно с помощью set, я понимаю, что кривая реализация, ну что я поделаю, раз такая формулировка задачи.
оператор < есть, просто я его убрала , чтоб код был короче.
Как то так:
C++
1
2
3
4
bool Info::operator < (const Info &a) const{
    if (key < a.key) return true;
    return false;
}
А с константным указателем попробую, спасибо

Добавлено через 30 минут
Проблема решена, сначала удалила объект ,а затем создала новый .Думаю, это не оч оптимальный вариант
0
Комп_Оратор)
Эксперт по математике/физике
8726 / 4473 / 600
Регистрация: 04.12.2011
Сообщений: 13,361
Записей в блоге: 16
08.05.2019, 17:59 4
Цитата Сообщение от hardequation Посмотреть сообщение
сначала удалила объект ,а затем создала новый
Это нормально. Если только множество и ни чего другого.
Цитата Сообщение от zayats80888 Посмотреть сообщение
хотя можно хранить константный указатель, и изменять объект, на который он указывает.
zayats80888, но сравнение указателей же по значению объекта - цели указателя. Как только измените объект множество завалится, если нарушится результат сравнения. Ваш совет о мапе совершенно хорош, если нужно писать по значению.
0
5232 / 2886 / 1208
Регистрация: 07.02.2019
Сообщений: 7,283
08.05.2019, 18:08 5
Цитата Сообщение от IGPIGP Посмотреть сообщение
но сравнение указателей же по значению объекта
я имел ввиду ключ по значению, а значение как указатель
0
Комп_Оратор)
Эксперт по математике/физике
8726 / 4473 / 600
Регистрация: 04.12.2011
Сообщений: 13,361
Записей в блоге: 16
08.05.2019, 19:37 6
Цитата Сообщение от zayats80888 Посмотреть сообщение
я имел ввиду ключ по значению, а значение как указатель
Поясните.
0
зомбяк
1562 / 1211 / 344
Регистрация: 14.05.2017
Сообщений: 3,925
08.05.2019, 20:09 7
zayats80888, для нового значения у элемента вся структура контейнера std::set в памяти будет (зачастую, хотя и не обязательно) перестроена для сохранения оптимальности поиска элементов. Поэтому никакого смысла хранить указатель на элемент, который будет модифицироваться, нет. И собственно удаление его из контейнера + вставка изменённого - основное решение.
0
5232 / 2886 / 1208
Регистрация: 07.02.2019
Сообщений: 7,283
08.05.2019, 20:33 8
Ну а так?:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
#include <set>
 
class X
{
    int k;
    double* v;
public:
    X() :k(0), v(new double) {}
    X(int key) : k(key), v(new double) {}
    X(const X& x) : X(x.k) { *v = *(x.v); }
    X& operator=(const X& x) { k = x.k; *v = *(x.v); }
    ~X() { delete v; }
 
    int key() const { return k; }
    double* value() const { return v; }
 
    bool operator<(const X& x) const { return k < x.k; }
};
 
int main()
{
    std::set<X> cont;
    cont.insert(X(1));
    cont.insert(X(2));
 
    for (auto& x : cont)
        *(x.value()) = 3.14;
 
    for (auto& x : cont)
        std::cout << x.key() << ' ' << *(x.value()) << std::endl;
}
Добавлено через 4 минуты
или вы имеете ввиду что сами объекты при перестройке будут реаллочиться?
0
зомбяк
1562 / 1211 / 344
Регистрация: 14.05.2017
Сообщений: 3,925
08.05.2019, 21:18 9
Цитата Сообщение от zayats80888 Посмотреть сообщение
или вы имеете ввиду что сами объекты при перестройке будут реаллочиться?
Именно это. Но перечитав документацию, убедился что был не прав:

std::set:insert

No iterators or references are invalidated. If the insertion is successful, pointers and references to the element obtained while it is held in the node handle are invalidated, and pointers and references obtained to that element before it was extracted become valid
std::set::erise

References and iterators to the erased elements are invalidated. Other references and iterators are not affected.
Добавлено через 6 минут
Но изменить ключ в любом случае нельзя, т.к. в этом случае "реаллок" как раз будет:

C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int main()
{
    std::set<X> cont;
    cont.insert(X(1));
    cont.insert(X(4));
    cont.insert(X(3));
 
    for (auto& x : cont)
        *(x.value()) = 3.14;
 
    for (auto& x : cont)
        std::cout << x.key() << ' ' << *(x.value()) << " " << static_cast<const void *>(&x) << std::endl;
    
    std::cout << std::endl;
    
    cont.erase(X(4));
    
    for (auto& x : cont)
        std::cout << x.key() << ' ' << *(x.value()) << " " << static_cast<const void *>(&x) << std::endl;   
    
     std::cout << std::endl;
    
    *(cont.insert(X(2)).first->value())=3.15;
   
    
    for (auto& x : cont)
        std::cout << x.key() << ' ' << *(x.value()) << " " << static_cast<const void *>(&x) << std::endl;
    
}
C++
1
2
3
4
5
6
7
8
9
10
1 3.14 0x784c60
3 3.14 0x784d20
4 3.14 0x784cc0
 
1 3.14 0x784c60
3 3.14 0x784d20
 
1 3.14 0x784c60
2 3.15 0x784cc0
3 3.14 0x784d20
1
Комп_Оратор)
Эксперт по математике/физике
8726 / 4473 / 600
Регистрация: 04.12.2011
Сообщений: 13,361
Записей в блоге: 16
09.05.2019, 00:03 10
zayats80888, TRam_ вот тут
Дружественная статическая функция из другого класса
в тегах кода есть комментарий с формулировкой задания. Там говорится о СУБД (!) не много ни мало. С этим уже ни чего не поделаешь, но из сентенций о парах ключ-значение видно, что std::map вполне подошла бы.
hardequation, в main:
C++
1
set <Info> DataBase;
Я не увидел оператора сравнения в классе Info. Почему это компилируется?
А оператор или предикат важны в вашем вопросе. Их нужно видеть, что бы ответить. От того как производится сравнение, зависит могли бы ли вы объявить часть полей не участвующих в сравнении (value в частности) mutable чтобы константные методы могли их менять (или функции друзья).
Но лучше не извращаться а использовать map.
1
зомбяк
1562 / 1211 / 344
Регистрация: 14.05.2017
Сообщений: 3,925
09.05.2019, 00:19 11
Цитата Сообщение от hardequation Посмотреть сообщение
work.cpp
C++
1
2
3
4
5
6
7
        for (set <Info> :: iterator it = db.begin(); it != db.end(); ++it){
            if ((*it).get_key() == user_key){ 
                cout << "The value of this key was replaced in data base\n"; 
                search_flag = 1; 
                change((*it), user_value);
            }
        }
C++
1
2
3
4
5
6
set <Info> :: iterator it = db.find( Info(user_key, string()) );
if(it != db.end()){ 
                cout << "The value of this key was replaced in data base\n"; 
                search_flag = 1; 
                change((*it), user_value);
            }
Добавлено через 1 минуту
Цитата Сообщение от IGPIGP Посмотреть сообщение
что std::map вполне подошла бы
Конечно же подошла бы. Но из первого поста
/* Формулировка задания
Написать СУБД (систему управления базой данных), хранящую данные в формате ключ-значение.
Тип ключа - строка, значение может любого типа, который можно распечатать (перегружен оператор <<).

СУБД имеет следующий набор операций:

SET ключ значение
если элемента с таким ключом нет - он добавляется

GET ключ
если элемент с таким ключом есть - выдается его значение, иначе - сообщение об отсутствии элемента с таким ключом

KEYS
распечатываются все ключи, хранящиеся в нашей БД
Работа с СУБД происходит в цикле, выход - по команде quit.

Хранение пар ключ-значение сделать на основе std::set. В классе, реализующем пару ключ-значение,
должен быть перегружен оператор <. Он должен быть константным и иметь константный аргумент.*/
Добавлено через 1 минуту
Цитата Сообщение от IGPIGP Посмотреть сообщение
А оператор или предикат важны в вашем вопросе.
См. пост №3.
1
Комп_Оратор)
Эксперт по математике/физике
8726 / 4473 / 600
Регистрация: 04.12.2011
Сообщений: 13,361
Записей в блоге: 16
09.05.2019, 10:03 12
Цитата Сообщение от TRam_ Посмотреть сообщение
См. пост №3.
нашёл:
Цитата Сообщение от hardequation Посмотреть сообщение
ператор < есть, просто я его убрала , чтоб код был короче.
Почему остальное не "убрала"?
hardequation, беда в том, что вы не понимаете как строится set (и map следовательно). hardequation, в отличие от std:: set, такие контейнеры как std::list или std::vector не имеют инварианта связывающего объекты с самим контейнером (предиката). А у 2-деревьев поиска он есть. И это коренной вопрос, о том, что в объекте - элементе контейнера, играет ключевую роль (роль ключа). В вашем случае это одно поле: key (для set'а построенного на предоставленном операторе <) и это значит что key трогать нельзя, а остальное - можно. Однако компилятор не обладает такими интеллектуальными задачами - анализировать предоставленный предикат и выяснять, что ключевое, а что - нет. Он парень простой и накладывает константность на весь объект. Как с этим бороться было сказано. Но если вы не связаны тем, что класс уже написан и ни чего нельзя в нём трогать, то напишите всё на map'е и будет вам счастье.
0
IT_Exp
Эксперт
87844 / 49110 / 22898
Регистрация: 17.06.2006
Сообщений: 92,604
09.05.2019, 10:03
Помогаю со студенческими работами здесь

Дружественная функция-оператор, доступ к закрытым полям класса
У меня есть класс MyClass, вот код: class MyClass { int x, y; public: MyClass(); friend...

Преобразование типа аргумента в тип класса и дружественная функция
Добрый день. Есть код: class Cls { private: double x; public: Cls(); Cls(double);

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

Почему дружественная функция не может обратиться к protected полю класса?
Не могу понять, почему функция не может обратиться к protected полю класса: class Ships {...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
12
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2022, CyberForum.ru