Страницы

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

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

суббота, 11 апреля 2020 г.

Как создать экземпляр вложенного класса внутри которого есть вызов конструктора?

#java #классы

                    
Нужно создать экземпляр такого вложенного класса (причем, нельзя трогать код вне
main() метода):

public class Outer {
    public static void main(String[] args){
        // код можно добавить только сюда
    }

    public static class Inner {
        Inner Inner = new Inner();

        public Inner() {
            System.out.println("Test");
        }
    }
}


казалось бы, экземпляр вложенного класса создается таким образом:

Outer.Inner inner = new Outer.Inner();


но вызов конструктора внутри вложенного класса все это ломает.
Как обойти эту проблему и таки создать экземпляр вложенного класса из main() метода?
    


Ответы

Ответ 1



Disclaimer: всё нижеописанное противоестественно, это ни в коем случае не следует применять на практике, если только вы не пишете низкоуровневые инструменты для Java-разработчиков. Как бы странно это не звучало, но Java можно заставить создать экземпляр объекта, не вызывая никаких конструкторов, в том числе конструкторов его полей. Делается это при помощи проприетарного API реализации JRE от Oracle (Sun). Ключом ко всему является скрытый в дебрях проприетарного кода (и посему не описанный в документации JDK) класс ReflectionFactory: ReflectionFactory rf = ReflectionFactory.getReflectionFactory(); Constructor objDef = Object.class.getDeclaredConstructor(); Constructor intConstr = rf.newConstructorForSerialization(Inner.class, objDef); Inner inner = Inner.class.cast(intConstr.newInstance()); Теперь у нас есть экземпляр класса Inner, в котором все поля имеют значение null: System.out.println(inner.getClass()); // class Outer$Inner System.out.println(inner.inner); // null Документация из OpenJDK: http://www.docjar.com/docs/api/sun/reflect/ReflectionFactory.html Источник: http://www.javaspecialists.eu/archive/Issue175.html P. S. Вообще, задача очень странная. Либо существует более простой (но почему-то неочевидный для меня) способ её решения, либо она предназначается для крутых спецов по Java Reflections API и задаётся на собеседованиях в компании вроде Zeroturnaround.

четверг, 9 апреля 2020 г.

Работа перегруженного оператора во время инициализации экземпляра класса [закрыт]

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

                            
             
                
                    
                        
                            Закрыт. Этот вопрос не по теме. Ответы на него в данный
момент не принимаются.
                            
                        
                    
                
            
                    
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он соответствовал тематике «Stack Overflow на русском».
                        
                        Закрыт 4 года назад.
                    
                
        

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

#include `
using namespace std;`

class Matrix
{
private:
    int **Arr;
    int Size;
public:
    Matrix(int SizeOfMatrix);
Matrix operator = (Matrix &);
};

int main()
{
    Matrix A(4);
    Matrix B(4) = A;
    system("pause");
    return 0;
}
Matrix::Matrix(int SizeOfMatrix)
{
    Size = SizeOfMatrix;
    Arr = new int *[Size];
    for (int i = 0; i < Size; i++)
    {
        Arr[i] = new int[Size];
        for (int j = 0; j < Size; j++)
            Arr[i][j] = rand() % 50;
    }
}
Matrix Matrix :: operator =(Matrix &A)
{
    for (int i = 0; i < Size; i++)
    {
        for (int j = 0; j < Size; j++)
            Arr[i][j] = A.Arr[i][j];
    }
    return *this;
}

    


Ответы

Ответ 1



Прежде всего хотел бы отметить, что данное предложение Matrix B(4) = A; некорректное и не должно компилироваться. Фактически в данном предложении имеется два инициализатора: 4 и A. Но даже если вы правильно напишите Matrix A(4); Matrix B = A; то здесь вызывается не оператор присваивания, как вы думаете, а конструктор копирования, который определен компилятором неявно, и который просто почленно копирует члены данных объектов. Когда вы динамически в классе распределяете память, то есть используете указатели, то вы должны определить явно по крайней мере конструктор копирования, копирующий оператор присваивания и деструктор. В противном случае поведение вашей программы может оказаться неопределенным. Например, в результате использования конструктора копирования, созданного компилятором неявно, может оказаться, что два объекта имеют указатели на одну и ту же область памяти, а потому при удалении этих объектов будет осуществлена попытка удалить одну и ту же память дважды. Ваш класс может выглядеть следующим образом class Matrix { private: int **Arr; size_t Size; public: explicit Matrix( size_t ); Matrix( const Matrix & ); ~Matrix(); Matrix & operator = ( const Matrix & ); }; Matrix::Matrix( size_t SizeOfMatrix ) : Arr( nullptr ), Size ( SizeOfMatrix ) { if ( Size ) { Arr = new int *[Size]; for ( size_t i = 0; i < Size; i++ ) { Arr[i] = new int[Size]; for ( size_t j = 0; j < Size; j++ ) Arr[i][j] = rand() % 50; } } } Matrix::Matrix( const Matrix &rhs ) : Arr( nullptr ), Size ( rhs.Size ) { if ( Size ) { Arr = new int *[Size]; for ( size_t i = 0; i < Size; i++ ) { Arr[i] = new int[Size]; for ( size_t j = 0; j < Size; j++ ) Arr[i][j] = rhs.Arr[i][j]; } } } Matrix::~Matrix() { for ( size_t i = 0; i < Size; i++ ) delete [] Arr[i]; delete [] Arr; } Matrix & Matrix::operator =( const Matrix &rhs ) { if ( this != &rhs ) { int **tmp = nullptr; if ( rhs.Size != 0 ) { if ( Size != rhs.Size ) { tmp = new int *[rhs.Size]; for ( size_t i = 0; i < rhs.Size; i++ ) { tmp[i] = new int[rhs.Size]; } } else { tmp = Arr; } for ( size_t i = 0; i < rhs.Size; i++ ) { for ( size_t j = 0; j < rhs.Size; j++ ) tmp[i][j] = rhs.Arr[i][j]; } } if ( Size != rhs.Size ) { for ( size_t i = 0; i < Size; i++ ) delete [] Arr[i]; delete [] Arr; } Arr = tmp; Size = rhs.Size; } return *this; }

суббота, 21 марта 2020 г.

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

#javascript #классы


Как получить значение из класса без методов используя только конструктор
Пример кода -

class Value {
  constructor() {
    /*
    код который бы вернул число
    */
  }
}
let result = new Value();
console.log(result /*Выводит например 3*/);

    


Ответы

Ответ 1



Как-то так: class JobSeeker { i = -1; valueOf() { return this.i = this.i + 2; } } let inst = new JobSeeker(); console.log( inst == 1 && inst == 3 && inst == 5 );

Ответ 2



Можно вернуть либо ссылку на текущий объект, либо новый объект. Вернёт объект Value: class Value { constructor (name){ this.name = name; // Аналогично отработает без return return this.name; } } // Деструктуризация const {name} = new Value('Ivan'); console.log(name); Вернёт новый объект: class Value { constructor (name){ this.name = name; return {name}; } } const {name} = new Value('Ivan'); console.log(name); Примитив вернуть нельзя. Аналогичный вопрос.

вторник, 17 марта 2020 г.

ООП ради ООП

#ооп #классы


Доброго времени суток!
Есть, к примеру, класс Cat, который реализует интерфейс Movable, инкапсулирует цвет
и прочее.    Имеет ли смысл создавать подклассы BlackCat, WhiteCat и т.д., которые,
по сути дела, ничего нового не привносят? Правильно ли в таком случае создавать enum
Сats и уже конкретный экземпляр enum'а (со своими полями) передавать в конструктор
класса Cat, а там присваивать значения полей?    


Ответы

Ответ 1



Переменные аспекты (условия/реализации или как удобней их называть) лучше всего всегда отделять от тех, которые являются постоянными. Это правило как один из принципов проектирования. Если следовать ему, то тут уж надо выделять отдельный интерфейс с реализациями цвета или, как предложили, создать перечисление. Например, если цвет будет переливающимся и становиться другим в зависимости от условий (допустим, от освещенности комнаты, где находится кошка), то уже перечислением не обойтись, а надо делать интерфейс и реализовывать отдельное поведение для отдельных цветов кошки. В итоге у нас может получиться один абстрактный класс или интерфейс Cat с реализацией, например, BadCat и GoodCat. Также будет интерфейс Colorable и конкретные реализации, например, WhiteColor, BlackColor.

Ответ 2



Говорить о сферических котах Шредингера в вакууме довольно сложно, поскольку не факт, что удастся перенести полученный в этом вопросе опыт в реальное приложение. Есть ряд простых, которые могут сказать, стоит или нет использовать наследование, или некоторый аспект должен быть лишь свойством. Отношение "Является" против отношения "является свойством". Итак, можно ли сказать, что черный кот является котом в том же смысле, что и "кот является млекопитающим"? Т.е. является ли цвет некоторой характеристиков поведения моделируемой сущности, которая является неизменной во времени? Можно ли сказать, что "черные коты" образуют определенную группу животных (котов), принадлежность к которым существенно меняет их собственное поведение? Или же можно сказать, что цвет - это один из атрибутов конкретного экземпляра, который, к тому же, может поменяться со временем? Поскольку второе высказывание кажется более логичным, то и предпочтение стоит отдать свойству Color, а не подклассам BlackCat, RedCat и т.д. У этого подхода есть ряд очень важных следствий. Когда что-то моделируется с помощью класса, то созданный объект намертво к нему привязывается. У созданного объекта кот нельзя поменять цвет, что вполне может быть реальным при моделировании некоторого поведения (например, кот постарел, его обрили). К тому же, иерархии плохо расширяются. Это значит, что для добавления нового цвета придется изменять иерархию, что существенно сложнее добавления нового перечисления или путем просто использования другого значения из стандартного перечисления Color. Наследование предназначено для моделирования некоторого подмножества в общем семействе объектов. Также, наследование является очень жесткой связью, которую невозможно изменить после создания объекта, а также сложно расширять. Большинство современных языков не обладают множественным наследованием реализации, что не позволит в случае наследования цветастости котов, добавить другую "проекцию" наследования, например, по породе котов или другому признаку.

воскресенье, 15 марта 2020 г.

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

#cpp #классы


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

// файл Complex.h
#include "Float.h"
class Complex
{
private:
    Float Re, Im;
public:
    Complex(Float re, Float im);
    void readFloat(Float);
};
// файл Complex.cpp
#include "Complex.h"
#include 
using namespace std;
void Complex::readFloat(Float)
{
    cout << "Чтение из дружественной функции: " << Re.Num;
}
// файл Float.h
class Float
{
private:
    float Num;
public:
    Float(float num);
    friend void Complex::readFloat(Float);
};

    


Ответы

Ответ 1



Не получается потому, что для объявления дружественного метода требуется полное определение Complex, ведь вы обращаетесь к его члену. Самый простой вариант - сделать другом не метод, а весь класс Complex: // файл Float.h class Float { private: float Num; public: Float(float num); friend class Complex; }; Если, всеж таки, необходим именно дружественный метод, тогда нужно обеспечить чтобы полное определение Complex гарантированно происходило до определения Float. Это можно сделать, применив технику с предварительным объявлением (forward declaration) Float в файле Complex.h: // файл Complex.h // #include "Float.h" // этот инклюд нужно убрать class Float; // предварительное объявление class Complex { private: Float * Re; Float * Im; public: Complex(Float & re, Float & im); ~Complex(); void readFloat(Float &); }; // файл Complex.cpp #include #include "Complex.h" #include "Float.h" // а здесь он нужен void Complex::readFloat(Float&) { std::cout << "Чтение из дружественной функции: " << Re.Num; } // файл Float.h #include "Complex.h" // мы обращаемся к членам Complex // поэтому он должен быть объявлен заранее class Float { private: float Num; public: Float(float num); friend void Complex::readFloat(Float&); }; Техника работает следующим образом. Так как в файле Complex.h используются только указатели и ссылки на Float компилятору для вычисления размеров этих переменных и полей не требуется полное определение класса Float. Ведь размер указателей и ссылок фиксированный и заранее известен, вне зависимости от того, на что они указывают или ссылаются. Компилятору нужно только намекнуть что понимается под именем Float то есть что он класс, а не структура например. Компилятор не может работать с неизвестными именами. Из-за этой тонкости, можно убрать #include "Float.h" из файла Complex.h. Однако, когда мы обращаемся к членам Float необходимо его полное определение, поэтому в файле Complex.cpp обязательно наличие #include "Float.h". После всего этого можно безопасно добавлять #include "Complex.h" в файле Float.h. В результате всех этих манипуляций получается, что класс Complex всегда определен до Float, хотя объявлен Float раньше Complex. Ну и в итоге, как вы помните, строгий порядок определений классов позволяет нам законно объявить дружественным метод Complex::readFloat.

Присвоение значения переменной при объявлении в классе

#cpp #классы #инициализация


Есть класс:

class A
{
  public: 
      static int count;

      A()
      {
          count++; // При создании каждого объекта эта переменная должна увеличиваться
      }
};


Надо присвоить значение этой переменной 0, до вызова конструктора. Но если это сделать
вот так:

static int count = 0;


То компилятор ругается. Как обойтись в такой ситуации?
    


Ответы

Ответ 1



Просто надо правильно определить эту переменную вне класса class A { public: static int count; A() { count++; // При создании каждого объекта эта переменная должна увеличиваться } }; //... int A::count = 0; Вы также можете опустить инициализатор, так как переменная в любом случае будет инициализирована нулем: int A::count; Если определение класса помещено в заголовочный файл, то определение статической переменной нужно поместить в один из программных модулей.

Свой List из array

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


Пытаюсь создать свой list из array

есть структура 

template 
struct Node {
    T data;
    Node* next; 
};


собственно функция 

template 
Node* arrayToList(const T tab[], size_t size){

Node *node = new Node;
for(int i=0;idata = tab[i];
node->next = new Node;
}
}


в main 

    int tabi[] = {2,1,4,3,6,5,7,8};
size_t sizei = sizeof(tabi)/sizeof(tabi[0]);
Node *listAi = arrayToList(tabi,sizei);


так вот проблема в том что не могу править созданный экземпляр Node тут node->next
= new Node; я создаю экземпляр , а как в него значение из массива занести ?  
    


Ответы

Ответ 1



Не понятно, в чем состоит ваша проблема Поэтому я предложу код, который позволяет заполнить список элементами массива. Надеюсь, что если это не то, что вам нужно, то вы об этом сообщите мне в своем комментарии к ответу. Вот демонстрационная программа #include template struct Node { T data; Node *next; }; template Node * arrayToList( const T a[], size_t n ) { Node *head = nullptr; Node **current = &head; for ( size_t i = 0; i < n; i++, current = &( *current )->next ) { *current = new Node { a[i], nullptr }; } return head; } template void displayList( Node *head ) { for ( ; head; head = head->next ) std::cout << head->data << ' '; } int main() { int a[] = { 2, 1, 4, 3, 6, 5, 7, 8 }; const size_t N = sizeof( a ) / sizeof( *a ); for ( int x : a ) std::cout << x << ' '; std::cout << std::endl; Node *list = arrayToList( a, N ); displayList( list ); std::cout << std::endl; } Ее вывод на консоль: 2 1 4 3 6 5 7 8 2 1 4 3 6 5 7 8 Если ваш компилятор не поддерживает список инициализации для оператора new, то предложение *current = new Node { a[i], nullptr }; вы можете заменить на следующие предложения *current = new Node; ( *current )->data = a[i]; ( *current )->next = nullptr; Либо вы можете написать для класса Node конструктор, чтобы можно было бы опять записать все в одну строчку, как, например *current = new Node( a[i], nullptr );

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

Оптимальное использование статических полей при проектировании классов

#c_sharp #классы


Есть базовый абстрактный класс и 3 других класса, которые последовательно наследуют
реализацию предыдущего (BaseAbstract->A->B->C). При создании нового экземпляра одного
из этих трех классов, задействуется статическое поле (счетчик) в соответствующем классе
для подсчета суммарного количества экземпляров конкретного класса, вывод которого осуществляется
через статический метод.

abstract class BaseAbstract
{
    public BaseAbstract()
    {
        //инициализация полей абстрактного класса

        IncStaticField();   //вызов абстрактного метода в конструкторе абстрактного
класса
    }
    protected abstract void IncStaticField();
}

class A : BaseAbstract 
{
    private static int instNo = 0;  //стат. поле

    protected override void IncStaticField()
    {
        A.instNo = A.instNo + 1;   //тип указываю здесь лишь для "пояснения" собственных
намерений
    }

    public static string Info()     //вывод кол-ва созданных экземпляров конкретного
класса
    {
        return "Общее количество созданных экземпляров класса A: " + instNo;
    }
}

class B : A
{
    private static int instNo = 0;

    protected override void IncStaticField()
    {
        B.instNo = B.instNo + 1;
    }

    public static new string Info()     // замещение
    {
        return "Общее количество созданных экземпляров класса B: " + instNo;
    }
}

class C : B
{
    //аналогичная реализация, как в классе B
}


Данная реализация успешно подсчитывает кол-во созданных экземпляров для каждого класса
и выводит их значения через вызов метода Info() конкретного класса.

1) Насколько безопасным является вызов абстрактного (виртуального) метода в конструкторе,
благодаря которому и происходит увеличение статического поля конкретного класса?

2) Можно ли упростить реализацию метода IncStaticField(), дабы не приходилось объявлять
его в каждом классе лишь для указания класса статического поля которое следует увеличить?
    


Ответы

Ответ 1



Для начала пару слов про ваш код: public static new string Info() // замещение new в данном случае не требуется, т.к. CLR, вообще, и C#, в частности, не поддерживают наследование статических членов, соответственно никакой проблемы с одноименными статическими методами в классах наследниках не возникнет. 1) Насколько безопасным является вызов абстрактного (виртуального) метода в конструкторе, благодаря которому и происходит увеличение статического поля конкретного класса? Если абстрактный/виртуальный метод корректно реализован/переопределен с использованием override, то все будет нормально. Но проблема с вероятностью возникнет, если виртуальный метод не переопределен в классе и вызывает метод базового класса, который еще не был инстанцирован. Поэтому в целом такой подход не желателен, т.к. мы не можем заставить производный класс принудительно переопределить виртуальный метод. 2) Можно ли упростить реализацию метода IncStaticField(), дабы не приходилось объявлять его в каждом классе лишь для указания класса статического поля которое следует увеличить? Если метод используется исключительно для увеличения счетчика объектов, то почему бы не заменить его на обычный instNo++ в конструкторе? Таким образом определять метод в каждом классе не нужно. А вот более сложную логику, если она есть/нужна, уже можно выпилить в отдельный метод, и переопределять его по мере необходимости. Ну и напоследок полезная статья про статические члены классов и классы: Эрик Браун Семь советов по применению статических полей, методов и классов

Ответ 2



Как вариант, могу предложить завести в самом базовом классе одно статическое поле -- словарь (Dictionary) с ключом -- тип класса либо название типа класса, и значением int. И один метод (невиртуальный), который и будет всё делать. В метод передаём Type. Принцип работы метода такой: увеличить счётчик в словаре для переданного типа, затем по reflection взять для этого типа тип-родитель, увеличить счётчик в словаре для него итд. Ну и обеспечить вызов этого метода-счётчика из конструктора каждого порождённого класса. Плюсы -- никаких виртуальных вызовов в конструкторе. Минусы -- использование рефлексии, снижающее производительность.

суббота, 7 марта 2020 г.

Доступ к закрытому полю класса извне

#cpp #классы


Здравствуйте, нашел на неком ресурсе подобный код(немного его модифицировал под себя):

#include 

using namespace std;

class PrivateClass
{
public:
    PrivateClass(int a, char b)
        : privateInt(a), privateChar(b) {}
    int     getInt() {return privateInt; }
    char    getChar() {return privateChar; }
private:
    int     privateInt;
    char    privateChar;
};

class Stealer
{
public:

    int &stealInt(PrivateClass &clss)
    {
        return ((Stealer *) (&clss))->stealedInt;
    }

    char &stealChar(PrivateClass &clss)
    {
        return ((Stealer *) (&clss))->stealedChar;
    }

private:
    int     stealedInt;
    char    stealedChar;
};

int main()
{
    PrivateClass    prVar(20, 'a');
    Stealer         stVar;
    int &stlInt     = stVar.stealInt(prVar);
    char &stlChar   = stVar.stealChar(prVar);
    cout << "Private Class:\nInt = " << prVar.getInt() << "\nChar = " << prVar.getChar()
<< endl;

    cout << "\nStealed:\nInt = " << stlInt << "\nChar = " << stlChar << endl;
    stlInt  = -65;
    stlChar = 'q';
    cout << "Int = " << stlInt << "\nChar = " << stlChar << endl;
    cout << "\nPrivate Class:\nInt = " << prVar.getInt() << "\nChar = " << prVar.getChar()
<< endl;
    return 0;
}


Результат:

Private Class:
Int = 20
Char = a

Stealed:
Int = 20
Char = a
Int = -65
Char = q

Private Class:
Int = -65
Char = q


Как я понял, класс Stealer как-то получил доступ к закрытым полям класса PrivateClass
да ещё и по ссылке, что дало возможность менять значения переменной "извне". Увы, я
внятного ответа нигде не нашел. Что это может быть?
    


Ответы

Ответ 1



Тут все совершенно тривиально, что может и быть причиной того, что объяснения отсутствуют. Класс Stealer просто переинтерпретирует память объекта типа PrivateClass как память объекта типа Stealer. Расположение полей класса Stealer, согласно задумке, должно совпадать с расположением полей класса PrivateClass. Это означает, что, согласно задумке, после такой переинтерпретации, смотря через поля класса Stealer, мы будем "видеть" в памяти соответствующие поля класса PrivateClass. Подобные трюки нарушают требования strict aliasing языка C++. Код порождает неопределенное поведение и в общем случае неработоспособен. P.S. Прием, позволяющий легально обойти защиту, предоставляемую private я приводил вот здесь: https://ru.stackoverflow.com/a/656542/182825. В применении к данному случаю: http://coliru.stacked-crooked.com/a/49db7cfe2b1bc36b

Ответ 2



Грязный хак :) Просто сделан аналогичный по структуре класс, и область памяти, занятая одним классом, объявлена как область памяти, занятая другим классом, который позволяет обратиться к его данным, а уж открытые они там или закрытые... Класс не "получил доступ", он подменил объект другого класса собой. Вернее, даже не объект, а указатель на объект другого класса объявил, как указатель на объект своего класса - с такой же внутренней структурой. Грубо говоря, подделал паспорт :)

четверг, 5 марта 2020 г.

Как вызвать функцию класса внутри js класса [закрыт]

#javascript #классы #ecmascript_6


        
             
                
                    
                        
                            Закрыт. Этот вопрос не по теме. Ответы на него в данный
момент не принимаются.
                            
                        
                    
                
                            
                                
                
                        
                            
                        
                    
                        
                            Хотите улучшить этот вопрос? Переформулируйте вопрос,
чтобы он соответствовал тематике «Stack Overflow на русском».
                        
                        Закрыт 2 года назад.
                                                                                
           
                
        
Например: 

 class Car(){
    weight(){
      alert('100kg');
    }
    getWeight(){
      this.weight()   //не работает
   }
}


Как это сделать правильно?
    


Ответы

Ответ 1



Этот вопрос следует закрыть, т.к. проблема вызвана опечаткой class Car { weight(){ alert('100kg'); } getWeight(){ this.weight() //не работает } } (new Car()).getWeight();

Ответ 2



class Car { weight() { console.log('100kg'); } getWeight(){ this.weight(); } } var car = new Car; car.getWeight();

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

#cpp #ооп #классы #исключения


Объясните почему при вводе 0  и выбросе исключения Base, оно не отлавливается блоком
Derived1?

int main()
{
    int number = 0;

    for (;;)
    {
        try
        {
            cin >> number;
            cout << number << " ";

            switch (number)
            {
                case 0:
                    throw Base();

                case 1:
                    throw Derived1();

                case 2:
                    throw Derived2();
            }
        }
        catch (Derived1 /*exception*/)
        {
            cout << "Exception of derived class" << endl;
        }
        catch (Base /*exception*/)
        {
            cout << "Exception of Base class" << endl;
        }
    }

    return 0;
}

    


Ответы

Ответ 1



Потому что Base не является Derived, а вот Derived является Base. Вот если бы вы написали (кстати, перехватывайте исключения по ссылке, иначе столкнетесь со срезкой) catch (Base& /*exception*/) { cout << "Exception of Base class" << endl; } catch (Derived1& /*exception*/) { cout << "Exception of derived class" << endl; } то тут и Base, и Derived ловились бы первым блоком - Base.

среда, 4 марта 2020 г.

Клонирование объектов Java

#java #ооп #классы #объекты #клонирование


В классе main есть: 
 1) класс сlass1 в котором есть двумерный массив и еще несколько переменных 
 2) функция func(class1 cl) которая принимает объект класса но возвращает другой
тип данных (float), при этом внутри тела функции проводятся манипуляции с двумерным
массивом класса что бы получить ответ

Так вот, проблема в том, что если я создам экземпляр класса, вызову функцию и передам
его туда, то после этого исходный массив объекта меняется (Что мне естественно не нужно) 
Я нашел решение в виде метода clone(), но хеш-код объектов то стал другим, а вот
хеш-код полей внутри клона объекта тот же, и соответственно при изменении массива одного
объекта изменяется массив другого 

Подскажите пожалуйста как решить эту проблему?
    


Ответы

Ответ 1



Сделайте class1 сериализуемым (добавьте implements Serializable в его определение). Сохраняем объекта класса в поток байтов: ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream ous = new ObjectOutputStream(baos); ous.writeObject(new class1()); ous.close(); теперь в func можно передать (class1)new ObjectInputStream(new ByteArrayInputStream(baos.toByteArray())).readObject() Таким образом объект класса сохраняется в поток из которого восстанавливаться независимый клон.

Ответ 2



Чтобы метод clone клонировал поля объекта, его нужно переопределить в самом классе. По умолчанию он оперирует только примитивными типами и указателями Но Вам нужно просто скопировать массив. Для этого есть метод Arrays.copyOf() или банально вызвать clone для массива Вот тут описаны разные методы получения копии массива https://stackoverflow.com/a/36513254/5376639

Синхронизация между объектами Workflow

#delphi #классы #связи #workflow #биоинформатика


Создаю универсальный workflow для обработки наших геномных данных. Для этого пишется
более или менее стандартный в нашей среде workflow, что-то вроде такого:



Каждая панель на рисунке - отдельный экземпляр определенного класса (наследника TShape,
но не думаю, что это важно) со своими методами, графические и математические проблемы
вроде более-менее решены.
У каждого класса есть хотя бы один метод ввода или вывода (бывает, и по нескольку),
формат данных на входе и на выходе у разных классов - разные. Одному, условно говоря,
нужен массив строк, другому - просто строка, третий выдает самостоятельные массивы
строк и массивы целых и  т.п.   

Поэтому первый вопрос - как проверять соответствие типов данных? Вот хочу я сделать
связь между двумя экземплярами на рисунке, а вдруг нельзя! Либо совсем нельзя, либо
нужен еще посредник (конвертор или обработчик). Ничего иного не придумал, кроме как
ввести единый энумератор, в котором прописать все возможные варианты типов данных и
запрашивать у экземпляров, которые хочу связать, совпадают ли у них типы ввод-вывод
по энумератору или нет. Или можно проще придумать способ?

Второй момент. Если использовать TMemoryStream для обмена данными, то как я должен
обрабатывать такие ситуации:


вывод в Stream экземпляром-донором начат. Нужно сообщить экземпляру-акцептору, что
можно начинать обрабатывать данные. Как? Создать поток акцептора уснувшим, а затем
Resume?  
Экземпляр-акцептор обрабатывает данные быстрее, чем их записывает в Stream экземпляр-донор.
Через что разумнее обработать эту ситуацию? Через CriticalSection?

    


Ответы

Ответ 1



По первому вопросу: нормальное решение. По второму вопросу: Для синхронизации работы потоков придумано много разных объектов синхронизации, в том числе и события (TEvent) и критические секции (TCriticalSection). Делать потоку Resume - это не по феншую. Чтобы передавать данные через TMemoryStream между потоками и одновременно читать и писать, его нужно сделать потокобезопасным (добавить в него критическую секцию). Возможно, для этой задачи лучше подойдет использование каналов (pipe), но мне пока не доводилось с ними работать и я про них мало чего знаю. Итак, наш наследник от TMemoryStream: TMyCustomMemoryStream = class(TMemoryStream) private FLock: TCriticalSection; FCanReadUpTo: Int64; FAllDataWritten: Boolean; protected function GetSize: Int64; override; procedure SetSize(const NewSize: Int64); override; public constructor Create; destructor Destroy; override; property Lock: TCriticalSection read FLock; property CanReadUpTo: Int64 read FCanReadUpTo write FCanReadUpTo; property AllDataWritten: Boolean read FAllDataWritten write FAllDataWritten; end; { TMyCustomMemoryStream } constructor TMyCustomMemoryStream.Create; begin inherited Create; FLock := TCriticalSection.Create; end; destructor TMyCustomMemoryStream.Destroy; begin FLock.Free; inherited Destroy; end; function TMyCustomMemoryStream.GetSize: Int64; begin FLock.Acquire; try Result := inherited GetSize; finally FLock.Release; end; end; procedure TMyCustomMemoryStream.SetSize(const NewSize: Int64); begin FLock.Acquire; try inherited SetSize(NewSize); finally FLock.Release; end; end; Lock - критическая секция для доступа к данным. Как-то так: var DataStream: TMyCustomMemoryStream; DataStream.Lock.Acquire; try DataStream.Position := 0; DataStream.Write(...); DataStream.AllDataWritten := True; finally DataStream.Lock.Release; end; Чтобы другой поток мог читать данные, нужно периодически отпускать критическую секцию, а не писать все за один заход. Не забываем, что Position, после Lock.Release может быть совсем не таким, как мы ожидаем, так что его нужно будет запоминать в контексте каждого потока и устанавливать каждый раз в нужное место при записи или чтении. Теперь поток. который читает данные: TCustomThread= class(TThread) private FTerminatedEvent: TEvent; FCanReadData: TEvent; FData: TMyCustomMemoryStream; procedure DoReadData; function CheckAndWaitNewData(BytesReaded: Int64): Boolean; procedure ClearData; protected procedure Execute; override; procedure TerminatedSet; override; public constructor Create; destructor Destroy; override; procedure ReadData(Data: TMyCustomMemoryStream); end; { TCustomThread } constructor TCustomThread.Create; begin inherited Create(False); FTerminatedEvent := TEvent.Create(nil, True, False, ''); FCanReadData := TEvent.Create(nil, True, False, ''); end; destructor TCustomThread.Destroy; begin Terminate; if not Suspended then WaitFor; ClearData; FTerminatedEvent.Free; FCanReadData.Free; inherited Destroy; end; procedure TCustomThread.ClearData; begin FCanReadData.ResetEvent; FreeAndNil(FData); end; procedure TCustomThread.DoReadData; var DataPosition: Int64; CountToRead: Int64; begin DataPosition := 0; while CheckAndWaitNewData(DataPosition) do begin // read new data FData.Lock.Acquire; try CountToRead := FData.Size - DataPosition; FData.Position := DataPosition; DataPosition := DataPosition + FData.Read(Buffer, CountToRead); finally FData.Lock.Release; end; // process new data // ... end; // all data readed ClearData; end; function TCustomThread.CheckAndWaitNewData(BytesReaded: Int64): Boolean; begin // returns True on data available or False on AllDataWritten Result := FData.Size > BytesReaded; while not Result and not FData.AllDataWritten do begin // wait for 100 ms to check data available again if FTerminatedEvent.WaitFor(100) <> wrTimeout then Abort; Result := FData.Size > BytesReaded; end; end; procedure TCustomThread.Execute; var WOHA: TWOHandleArray; begin WOHA[0] := FCanReadData.Handle; WOHA[1] := FTerminatedEvent.Handle; try while not Terminated do begin if WaitForMultipleObjects(2, @WOHA, False, INFINITE) = WAIT_OBJECT_0 then DoReadData else Exit; end; except on EAbort do Exit; end; end; procedure TCustomThread.ReadData(Data: TMyCustomMemoryStream); begin if Assigned(FData) then raise Exception.Create('Thread is busy!'); FData := Data; FCanReadData.SetEvent; end; procedure TCustomThread.TerminatedSet; begin inherited TerminatedSet; FTerminatedEvent.SetEvent; end; Предполагается, что поток, читающий данные, ответственен за уничтожение потока данных (возможно не лучшее решение). Поток создается и ожидает события для начала чтения FCanReadData или события, сигнализирующего о необходимости завершения работы потока FTerminatedEvent. Метод Execute можно модифицировать, если нужно делать еще какую-то работу, тогда ожидание должно быть не бесконечным либо вообще нулевым. Чтение данных запускается вызовом метода ReadData. Чтение данных продолжается до тех пор, пока не закончатся данные, после чего, если в потоке данных не установлен признак AllDataWritten начинается ожидание поступления новых данных с проверкой наличия новых данных каждые 100 мс. Если данные закончились и установлен признак AllDataWritten, чтение данных прекращается и поток данных уничтожается.

Ответ 2



Да, пожалуй. У нас в пакетах, где используются workflow, у всех таких экземпляров есть компонент-хозяин (потомок от TScrollBox с добавленным свойством Canvas), в обязанности которого входят отрисовка всяких стрелочек, создание очереди, автораспределение по поверхности и т.п., и в том числе хранение энумератора с типами данных, а также автосоздание цепочки, если это возможно (Скажем, на входе SAM, а процессу нужны FASTA, тогда пользователю автоматически предлагается создать экземпляр посредника). Мы от потоков отказались, для нас это неудобно, используем собственные классы-контейнеры для массивов. Поскольку не все алгоритмы могут работать с частичными данными, то каждый экземпляр-акцептор имеет свойство в том же энумераторе, нужны ли ему данные полностью или он может брать частично, и если второе - начиная с какого количества уже созданных данных ему можно отсемафорить о начале работы. Поскольку акцепторы никогда не подходят близко к адресам вновь создаваемых данных, критические секции не используются, акцепторы предупреждаются через TEvent. Но если нужен именно TMemoryStream, то @kot-da-vinci , на мой взгляд, очень хорошо ответил.

Стоит ли создавать новый класс для вариации алгоритма?

#java #алгоритм #классы #условия


Есть класс А, который реализует некий алгоритм.
На главной форме есть чекбокс, при отметке которого алгоритм выполняется с небольшими
изменениями в логике.
Также могут появиться еще много чекбоксов, которые по-немногу меняют логику алгоритма.
Получается, для обработки всех чекбоксов в алгоритме класса А надо будет писать много
условий, код усложняется как визуально, так и для понимания.  

Или лучше создавать новые классы А2, А3 и т.д., которые "в чистом виде" будут реализовывать
свои вариации алгоритма?
    


Ответы

Ответ 1



На мой взгляд лучше создать новый класс под каждый алгоритм. Т.е. вы создаете интерфейс допустим такой: interface AlgoritmExecutor{ execute(); } И для каждого класса алгоритмов его реализуете, и сам алгоритм пишите в нем. В этом случае при добавлении нового алгоритма Вам надо будет написать новый класс, а не лезть в старый, добавляя новые условия. Но остается вопрос как выбирать нужный класс. Тут моё предложение не лучшее. Создать фабрику, которая будет принимать пачку boolean за каждый чекбокс. class AlgoritmFactory{ static AlgoritmExecutor chooseAlgoritm(boolean... checkbox){ //тут исходя из включенных чекбоксов выбираете нужный класс алгоритма } } Это удобно тем, что в слое вида вы обращаетесь к фабрике, передаете ей все чекбоксы и получаете выбранный алгоритм в виде интерфейса. public void algoritmAction(){ AlgoritmExecutor algoritm = AlgoritmFactory(checkbox1.isEnabled(), checkbox2.isEnabled(), checkbox3.isEnabled()); algoritm.execute(); } но фабрика становится не очень понятна для стороннего читателя из-за пачки булевых значений.

C# интерфейсы и абстрактные классы

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


Доброго!

Есть интерфейс IInterface,

Есть абстрактный класс его реализующий AClass

И есть абстрактный класс 

AClasses
{
  List Elements;

  void SomeMethod()
  {
    T _el = new T();
    _el.Some();
    Elements.Add(_el);
  }
}


Вот в методе SomeMethod() проблема так как это сделать нельзя. T должен наследоваться
от IInterface ну или на крайний случай от AClass. 

Так вот вопрос. Как заполнить List Elements в абстрактном классе
    


Ответы

Ответ 1



Например, при помощи ограничений на T: class AClasses where T : IInterface, new() { ... Это будет работать, если у ваших классов есть пустой конструктор. Если пустого конструктора нет, то создание экземпляров нужно делать как-нибудь по-другому, например, через фабрику или создающий делегат.

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

Абстрактные классы, Интерфейсы и наследование

#java #классы #объекты


Почему когда Абстрактный класс имплиментирует(реализовывает) 2-3 интерфейса к примеру,
он не имплиментирует(реализовывает) методы в себя(Абстрактный класс). То есть если
в обычном классе при имплиментации мы обязаны реализовать методы, то в Абстрактном
классе компилятор никак не ругается, что мы не реализовали методы. Пробовал через наследников
абстрактного класса достучаться до этих методов, да всё работает, но не пойму почему
мы их не определяем в абстрактном классе.
    


Ответы

Ответ 1



Интерфейс по идее должен содержать только сигнатуры методов, определяющие возможности того или иного объекта, к сожалению начиная с Java 8, вас могут смущать интерфейсы, содержащие default реализации методов, но не стоит на них обращать внимания, пока не поймёте, что вам это необходимо. Абстрактный класс имеет всё, что имеет обычный класс, однако один или более его методов не содержит реализации, но тем не менее он является почти полноценным. Т.е. он уже достаточно специфичен и описывает только малую часть моделей, однако он всё ещё обобщает некоторые свойства. Типичный пример приводят на животных. Интерфейс Movable может содержать метод move, однако каждое животное двигается по своему, кто-то летает, кто-то плавает. Далее абстрактным классом может выступать птица - Bird, которая будет содержать поле крыло - Wing. А далее мы реализуем конкретную модель цыплёнок - Chicken, который вроде и содержит крыло, однако летать не умеет, поэтому ему требуется реализовать метод move, не использовав поле Wing, а только перемещение по земле. Однако при написании класса сов - Owl, будем реализовывать метод move в виде полёта используя Wing.

Ответ 2



Абстрактным является класс, содержащий один или более абстрактных методов. Такой класс может содержать и обычные методы, наследоваться от другого абстрактного класса, а также имплементировать интерфейсы. Любой класс, который наследуется от абстрактного, обязан реализовать абстрактные методы предка, либо сам быть объявлен абстрактным, оставляя их реализацию потомкам. Полностью свойства интерфейса здесь рассматривать не будем, поясним только то, что касается вопроса. Интерфейс содержит методы без реализации (не считая методов по умолчанию). Имплементировать интерфейсы могут как обычные, так и абстрактные классы. Обычные классы обязаны реализовать методы интерфейса, но абстрактные могут это делать, а могут и нет. interface Iface1{ void method1(); } interface Iface2{ void method2(); } interface Iface3{ void method3(); } abstract class Abs implements Iface1, Iface2, Iface3{ //объявляем собственный абстрактный метод abstract void absMethod(); //объявляем метод интерфейса Iface1, но не реализуем //его здесь (имеем право, но не обязаны!) public abstract void method1(); //реализуем метод из интерфейса Iface2 прямо здесь public void method2(){ System.out.println("I'm method2!"); } /*метод method3 из интерфейса Iface3 мы даже не объявляем и оставляем его для реализации потомкам*/ } class Real extends Abs{ void absMethod(){ System.out.println("I'm a real absMethod!"); } public void method1(){ System.out.println("I'm a real Iface1.method1!"); } public void method3(){ System.out.println("I'm a real Iface3.method3!"); } } class PrimerAbs{ public static void main(String[] args){ Real real = new Real(); real.absMethod(); real.method1(); real.method2(); real.method3(); } } В данном примере объявлены три интерфейса, абстрактный класс, имплементирующий эти интерфейсы и обычный класс, который наследуется от абстрактного. В абстрактном объявлен свой собственный абстрактный метод absMethod(), объявлен метод method1 из интерфейса Iface1, но не реализован, т.к. здесь не обязательно (но можно) это делать, реализован метод method2 из интерфейса Iface2, что возможно, но не обязательно. Метод method3 из интерфейса Iface3 даже не объявлен и предоставлен для решения потомкам. Следует обратить внимание, что в абстрактном классе объявление методов из интерфейса, если они не реализуются в нём, должны быть с модификаторами public и abstract, иначе компилятор начнёт ругаться.

PHP: Как называется данная цепочка функций?

#php #классы #функции


Подскажите пожалуйста как называется данная цепочка функций и как её организовать? 

/*class Example*/
(new Example)->functionOne()->functionTwo();


и возможна ли такая связка?

/*class Example*/
(new Example)->functionOne($one)->functionTwo($two);


Лучше будет если направить на литературу. Я либо плохо гуглил, либо что-то не заметил.
    


Ответы

Ответ 1



Такой приём называется чейнингом. Можете почитать о нём, например на хабре. Его суть заключается в том, что методы возвращают ссылку на экземпляр объекта. Это может быть как объект, в котором этот метод вызывался (как у вас в примере), так и любой другой. /** * Все методы класса Example возвращают ссылку на себя */ class Example { function exampleFunctionOne() { echo "exampleFunctionOne\n"; return $this; } function exampleFunctionTwo() { echo "exampleFunctionTwo\n"; return $this; } } /** * Метод класса Example2 принимает в качестве аргумента экземпляр * класса Example и возвращают ссылку на него */ class Example2 { function example2FunctionOne($example) { echo "example2FunctionOne\n"; return $example; } } $e = new Example(); $e->exampleFunctionOne()->exampleFunctionTwo(); $e2 = new Example2(); $e2->example2FunctionOne($e)->exampleFunctionTwo();

Python Как узнать кому принадлежит объект через метод этого объекта

#python #python_3x #ооп #классы #объекты


Пример: есть объект Персона, у которого есть объект Голова. У Головы есть метод who_my_owner(),
который должен возвращать Персону, кому она принадлежит. Можно, конечно, использовать
уникальные идентификаторы, чтобы при создании головы, ей сообщался ид хозяина. Но есть
ли другой способ, чтобы уместить его в один метод who_my_owner() и без дополнительных
манипуляций с остальным кодом?

class Person:
    def __init__(self):
        self.head = Head()

class Head:
    def who_my_owner(self):
        pass

pers1 = Person()
pers2 = Person()
pers3 = pers1.head.who_my_owner() #должно быть эквивалентно pers3 = pers1

    


Ответы

Ответ 1



class Person: def __init__(self): self.head = Head(self) class Head(): def __init__(self, parent=None): super().__init__() self.parent = parent def who_my_owner(self): # pass return self.parent pers1 = Person() print(f"pers1 - {pers1}") pers2 = Person() pers3 = pers1.head.who_my_owner() #должно быть эквивалентно pers3 = pers1 print(f"pers3 - {pers3}") pers1 - pers3 -

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

Объекты классов-оболочек в Java

#java #классы #типы


Объект любого из классов-оболочек представляет собой полноценный экземпляр в динамической
памяти, в котором хранится его неизменяемое значение.
Что подразумевается под неизменяемым значением?
    


Ответы

Ответ 1



Неизменяемое значение - это значение примитива, для которого объект является оболочкой. К примеру, для int - оболочка java.lang.Integer для long - оболочка - java.lang.Long. Чтобы убедиться - открываем исходный код класса java.lang.Integer : /** * The value of the {@code Integer}. * * @serial */ private final int value; /** * Constructs a newly allocated {@code Integer} object that * represents the specified {@code int} value. * * @param value the value to be represented by the * {@code Integer} object. */ public Integer(int value) { this.value = value; } Вот это private final int value и есть неизменяемое значение. UPD: То есть объявляя Integer somevar = 1; на самом деле, выполняется нечто вроде: Integer somevar = new Integer(1); далее, если мы захотим поменять значение somevar = 2 - то переменная somevar будет указывать уже на другой объект, а не перезатрет значение предыдущего. а предыдущий объект, будет собран сборщиком сусора, если на него более не имеется ссылок

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); }