Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > ромбовидное наслежлвание


Автор: fuckingniger 3.11.2010, 00:26
ситуация такова:
           класс1
         /           \   
класс2            класс3
        \             /
          класс4

указатель на класс 1(по типу) нужно поставить на класс4
соответственно компилятор не знает  через какую ветку это ему делать ...как бы ему это сообщить???

Автор: Crafty 3.11.2010, 00:37
Используйте виртуальное наследование.
Код

#include <iostream>

class A
{
public:
  virtual void print() { std::cout << "class A" << std::endl; }
  virtual~A() { }
};

class B: virtual public A
{
public:
  void print() { std::cout << "class B" << std::endl; }
};
class C: virtual public A
{
public:
  void print() { std::cout << "class C" << std::endl; }
};
class D: public B, public C
{
public:
  void print() { std::cout << "class D" << std::endl; }
};

int main()
{
  A *b = new D;
  b->print();
} 

Автор: fuckingniger 3.11.2010, 00:41
так и сделано ...но дело в том что имеется массив указателей на класс 1 и нужно чтобы можно было указать на все классы ...т.к они производные.

то есть этот массив будет хранить указатели на все объекты этой иерархии

Автор: mes 3.11.2010, 00:47
1. static_cast, если известно что тип соответсвует,
2. dynamic_cast с проверкой соответсвия в рантайме
3. организовать архитектуру с сопоставлением иерархии объектов  к иерархии операций, 
подробнее в поиске по "двойная диспетчеризация"

Добавлено через 1 минуту и 27 секунд
тип наследования (ромбовидный) к проблеме не относится.. 


Автор: fuckingniger 3.11.2010, 01:08
static помог спасибо

Автор: mes 3.11.2010, 01:12
Цитата(fuckingniger @  3.11.2010,  00:08 Найти цитируемый пост)
static помог спасибо 

чтоб быть уверенными что он помог правильно, разберитесь внимательно когда нужно использовать статик_каст, а когда диамик_каст..

Автор: fuckingniger 3.11.2010, 01:42
Код

vector <worker*>workers(number_workers); 

Код

    worker_procent_of_sum* ptr = dynamic_cast <worker_procent_of_sum*> (workers[0]);


Код

 'worker' is not a polymorphic type
 see declaration of 'worker'


declaration of 'worker'
Код

#ifndef WORKER_H
#define WORKER_H
# include <string>
using namespace std;
class company;
struct name
{
    string first_name;
    string last_name;
};
class worker
{
protected:
    string first_name;
    string last_name;
    int time_of_work;
public:
    worker(string s1, string s2 , int time,company& company_1);
    string get_first_name();
    string get_last_name();
    void print();
};
#endif


Код

#ifndef WORKER_PROCENT_OF_SUM 
#define WORKER_PROCENT_OF_SUM 

# include "worker.h"
# include <iostream>
using namespace std;
class worker_procent_of_sum:public worker
{
public:
    worker_procent_of_sum(string s1,string s2,int d,company& company_1,double sum,double prc) ;
    void set_sum_all(double);
    void set_percent(double);
    double salary();
    void print ();
protected:
    double sum_all;
    double percent;
};
#endif



почему такая ошибка ?...ведь вроде бы всё правильно написано worker_procent_of_sum наследник  worker и почему тогда он не полтиорфный ???

Автор: mes 3.11.2010, 01:49
полиморфным класс становится при наличии хотя бы одной виртуальной функции ( в том числе и виртуального деструктора)..

Автор: fuckingniger 3.11.2010, 01:51
а dynamic_cast только к полиморфным можно применять? чем простые хуже?

Автор: mes 3.11.2010, 01:54
Цитата(fuckingniger @  3.11.2010,  00:51 Найти цитируемый пост)
а dynamic_cast только к полиморфным можно применять? чем простые хуже? 

тем что не хранят информацию о наследственных связях в рантайме.. 

Автор: fuckingniger 3.11.2010, 01:59
то есть если я щас возьму сделаю 1 полиморфную функцию в worker то всё заработает?

Добавлено через 3 минуты и 1 секунду
и правда ошибки не стало ...чудеса да и только 

Автор: mes 3.11.2010, 02:03
Цитата(fuckingniger @  3.11.2010,  00:59 Найти цитируемый пост)
то всё заработает? 

насчет всего не знаю, но эта ошибка пропадет ..
 smile 

Автор: fuckingniger 3.11.2010, 02:15
извините, что столько вопросов, но не клеится что-то сегодня .

имеется массив указателей на класс1 под названием workers

я при помощи static_cast преобразую указатель на класс4(из 1 поста) к классу1(чтоб хранить можно было в массиве) а потом для вызова специфических функций класса 4 делаю обратное преобразование при помощи dynamic_cast . вылетает след ошибка : 

http://savepic.net/77225.htm

Автор: Леопольд 3.11.2010, 08:10
Цитата(fuckingniger @  3.11.2010,  02:15 Найти цитируемый пост)
я при помощи static_cast преобразую указатель на класс4(из 1 поста) к классу1(чтоб хранить можно было в массиве)
От производного к базовому кастуется без вмешательсва программиста (т.е. без static_cast'а).
Код
struct Parent { virtual ~Parent() {} };
struct Child1: public Parent { char data; };
struct Child2: public Parent { int data; };

Child1 child;
Parent * pPtr = &child;
 (При этом указатель на таблицу виртуальных функций остаётся прежним, что приводит к вызову виртуальных функций из производного, в данном случае, если сделать delete pPtr; то будет вызван деструктор Child1.)
Обратного неявного преобразования нет, поэтому нужно указывать static_cast. Однако, это "опасное" действие, потому что можно сделать так:
Код
Child2 * cPtr = static_cast<Child2 *>(pPtr);
это приводит к UB (undefined behaviour - неопределённое поведение) Запускаем, и смотрим что получилось:
Код
struct Parent {
    virtual void foo() = 0;
protected:
    ~Parent() {} //разрушать объект через указатель на Parent нельзя
};
struct Child1: public Parent { virtual void foo() {std::cout<<"Child1"<<std::endl;} };
struct Child2: public Parent { virtual void foo() {std::cout<<"Child2"<<std::endl;} };

int main()
{
    Child1 child;
    Parent * pPtr = &child;
    Child2 * cPtr = static_cast<Child2 *>(pPtr);
    cPtr->foo();

    //delete pPtr; //error: ‘Parent::~Parent()’ is protected

    return 0;
}


Добавлено @ 08:24
Цитата(fuckingniger @  3.11.2010,  02:15 Найти цитируемый пост)
вылетает след ошибка 
Возможно, выскакивает std::bad_cast (правда он возникает когда приводишь ссылки, а не указательи). 
http://cplusplus.com/reference/std/typeinfo/bad_cast/
Поведение dynamic_cast не очень очевидно. В случае с указателями, исключения не возникает, но указатель устанавливается в NULL. В случае с ссылками, вылетает исключение bad_cast (просто потому что ссылку нельзя установить в NULL)
Код
struct Parent {
    virtual void foo() = 0;
    virtual ~Parent() {} //удалять через указатель на Parent можно
};
struct Child1: public Parent {
    virtual void foo() {std::cout<<"Child1"<<std::endl;}
    virtual ~Child1() {std::cout<<"~Child1"<<std::endl;}
};
struct Child2: public Child1 {
    virtual void foo() {std::cout<<"Child2"<<std::endl;}
    virtual ~Child2() {std::cout<<"~Child2"<<std::endl;}
};
// здесь Child2 наследуется от Child1

int main()
{
    Child1 * cPtr = new Child1;
    Parent & pRef = *cPtr;
    
    try{
        Child2 & cRef = dynamic_cast<Child2 &>(pRef);
        cRef.foo();
    }
    catch(std::exception &e)
    {
        std::cout<<e.what()<<std::endl;
    }

    delete &pRef;
    
    return 0;
}


Автор: baldina 3.11.2010, 11:20
Цитата(fuckingniger @  3.11.2010,  02:15 Найти цитируемый пост)
извините, что столько вопросов, но не клеится что-то сегодня .

Проверьте, включено ли в параметрах проекта runtime type info
нужно для работы dynamic_cast, по умолчанию в VC выключено

Автор: Леопольд 3.11.2010, 12:17
Цитата(baldina @  3.11.2010,  11:20 Найти цитируемый пост)
нужно для работы dynamic_cast, по умолчанию в VC выключено 
Тогда не компилировалось бы, нет? 
gcc так себя ведёт, если RTTI отключить.

Автор: fuckingniger 3.11.2010, 13:01
Цитата(Леопольд @  3.11.2010,  08:10 Найти цитируемый пост)
От производного к базовому кастуется без вмешательсва программиста (т.е. без static_cast'а).


Цитата(fuckingniger @  3.11.2010,  02:15 Найти цитируемый пост)
я при помощи static_cast преобразую указатель на класс4(из 1 поста) к классу1

Просто у меня почему-то не получается указателю на базовый класс(класс1) присвоить объект на самого дальнего наследника (класс 4) 

Цитата(Леопольд @  3.11.2010,  08:10 Найти цитируемый пост)
Поведение dynamic_cast не очень очевидно. В случае с указателями, исключения не возникает, но указатель устанавливается в NULL. В случае с ссылками, вылетает исключение bad_cast (просто потому что ссылку нельзя установить в NULL)


у меня указатели и только указатели

Добавлено через 11 минут и 16 секунд
Код


Проверьте, включено ли в параметрах проекта runtime type info
нужно для работы dynamic_cast, по умолчанию в VC выключено



http://savepic.net/94666.htm

также вроде всё???

Автор: fuckingniger 3.11.2010, 13:16
Код

# include <iostream>
# include <vector>
# include <string>
# include "company.h"
# include "worker_percent_plus_base.h"
using namespace std;
void input_worker_with_base(string &f,string &l,int &days, double& fix);
void input_worker_procent_of_sum(string &f,string &l,int &days, double& sum,double& prc);
void input_worker_percent_plus_base(string &f,string &l,int &days, double& fix, double& sum,double& prc);
int main ()
{
    company head_company;
    string s1,s2;
    int time,k,number_workers, index =0;
    double base , salary , procent ,sum_of_sale;
    bool is_end=false;
    cout << "input number workers in company >";cin>>number_workers;
    vector <worker*>workers(number_workers); 
    do {
        cout << "\ninput number\n\t1-add new worker"
                "\n\t9-exit "
                "\n\t>";cin>>k;
        switch(k)//главный
        {
        case 1:
            if (number_workers==index)
            {
                cout <<"error!!! State is full !!!"<<endl ;
                break;
            }
            cout << "\t\twhat type of worker?\n\t\t1-base\n\t\t2-procent\n\t\t3-base+procent\n\t\t>";cin>>k;
            switch(k)//на добавление какого типа работника
            {
            case 1:
                input_worker_with_base(s1,s2,time,base);
                workers[index++]=new worker_with_base(s1,s2,time,head_company,base);
                break;
            case 2:
                input_worker_procent_of_sum(s1,s2,time,sum_of_sale,procent);
                workers[index++]=new worker_procent_of_sum(s1,s2,time,head_company,sum_of_sale,procent);
                break;
            case 3:
                input_worker_percent_plus_base(s1,s2,time,base,sum_of_sale,procent);
            //    dynamic_cast<worker_percent_plus_base*>(workers[index]);
                workers[index++]=new worker_percent_plus_base(s1,s2,time,head_company,base,sum_of_sale,procent);
                break;
            }
            cout << "Worker add succes !!!"<<endl;
            if (number_workers==index)
                cout <<"state is full !!!"<<endl ; 
            break;
        case 9:
            is_end=true;
            break;
        }        

    }while(!is_end );
    //int* ptr=new int [10];  
    //worker* workers = new worker[k];
    /*worker_with_base base_1("Aleksandr","Ivanov",3,head_company,600);
    base_1.print();
    worker_procent_of_sum worker_proc_1("Anna","Ivanovna",134,head_company,20100,4);
    worker_proc_1.print();
    worker_percent_plus_base worker_proc_base_1("Igor","Egorov",600,head_company,400,20000,2);
    worker_proc_base_1.print();
    cout << "call salary()(worker_percent_plus_base) for Igor Egorov "<<worker_proc_base_1.salary()<<endl;
    cout << "call get_first_name()(worker) for Igor Egorov: "<<worker_proc_base_1.worker_procent_of_sum::get_first_name()<<endl;
    cout << "call set_percent(4)(worker_procent_of_sum) for Igor Egorov "<<endl;
    worker_proc_base_1.set_percent(4);
    cout << "call print(worker_percent_plus_base) for Igor Egorov "<<endl;
    worker_proc_base_1.print();
    cout << "call get_base()(worker_with_base) for Igor Egorov "<<worker_proc_base_1.get_base()<<endl;
    cout << "call set_base(200)(worker_with_base) for Igor Egorov "<<endl;
    worker_proc_base_1.set_base(200);
    worker_proc_base_1.print();
    head_company.print();*/
    return 0;
}
void input_worker_with_base(string &f,string &l,int &days, double& fix)
{
    cout << "\t\t\tinput first name > ";cin >> f;
    cout << "\t\t\tinput last name > ";cin >> l;
    cout << "\t\t\tinput experience(days)> ";cin >> days;
    cout << "\t\t\tinput base salary > ";cin >> fix;
}
void input_worker_procent_of_sum(string &f,string &l,int &days, double& sum,double& prc)
{
    cout << "\t\t\tinput first name > ";cin >> f;
    cout << "\t\t\tinput last name > ";cin >> l;
    cout << "\t\t\tinput experience(days) > ";cin >> days;
    cout << "\t\t\tinput sum of sale > ";cin >> sum;
    cout << "\t\t\tinput procent of sale > ";cin >> prc;
}
void input_worker_percent_plus_base(string &f,string &l,int &days, double& fix, double& sum,double& prc)
{
    input_worker_procent_of_sum(f,l,days,sum,prc);
    cout << "\t\t\tinput base salary > ";cin >> fix;
}

в строке
Код

workers[index++]=new worker_percent_plus_base(s1,s2,time,head_company,base,sum_of_sale,procent);

ошибка
Код

 error C2594: '=' : ambiguous conversions from 'worker_percent_plus_base *' to 'worker *'



worker - родитель 
а worker_percent_plus_base это класс4  на схемке из 1 поста

Добавлено через 2 минуты и 33 секунды
http://rghost.ru/3131552

вот весь проект на всякий случай

Автор: mes 3.11.2010, 13:52
Цитата(fuckingniger @  3.11.2010,  12:01 Найти цитируемый пост)
Просто у меня почему-то не получается указателю на базовый класс(класс1) присвоить объект на самого дальнего наследника (класс 4) 


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

Добавлено через 1 минуту и 10 секунд
Цитата(fuckingniger @  3.11.2010,  12:16 Найти цитируемый пост)
ошибка

та же причина..

Добавлено через 2 минуты и 2 секунды
я то думал, Вам уже надо было давно сдать.. если время сть, вместотого чтоб костыли городить, может лучше сделать по человечески ?

Автор: fuckingniger 3.11.2010, 13:58
я понимаю что надо разобрться с виртуальным наследованием но если коротко то везде надо написать virtual public вместо public?


Автор: mes 3.11.2010, 14:06
Цитата(fuckingniger @  3.11.2010,  12:58 Найти цитируемый пост)
я понимаю что надо разобрться с виртуальным наследованием но если коротко то везде надо написать virtual public вместо public?

т.е. еще глубже в лес ? может не стоит так необдуманно ?

Автор: Master01 3.11.2010, 14:16
Цитата(fuckingniger @ 3.11.2010,  00:26)
ситуация такова:
           класс1
         /           \   
класс2            класс3
        \             /
          класс4

указатель на класс 1(по типу) нужно поставить на класс4
соответственно компилятор не знает  через какую ветку это ему делать ...как бы ему это сообщить???


Если без виртуального наследования, то можно и так

(Class4*) static_cast<Class2*>(Class1*)

т.е. сначала явно кастите к нужной ветке, а потом уже простой каст на базовый класс

Автор: fuckingniger 3.11.2010, 14:30
теперь уже ошибка появилась в реализации класса 4 он не может вызвать конструкторы для класса 2 и 3

Добавлено через 1 минуту и 52 секунды
mes

я то сдал. Просто дело в том что 1 предметная область и в неё пихать нада сначала просто классы потом наследование затем полиморфизм ...перегузка ...и шаблонами должно всё ограничиться

Добавлено через 2 минуты и 40 секунд
Master01
пробовал так  - не работает у меня

Добавлено через 9 минут и 9 секунд
в классе 4 пишет что не доступного default конструктора  для самого главного класса worker

Автор: mes 3.11.2010, 14:46
Цитата(fuckingniger @  3.11.2010,  13:30 Найти цитируемый пост)
в классе 4 пишет что не доступного default конструктора  для самого главного класса worker 

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

добавьте в список инициализации конструктора 4го класса инициализацию виртуального класса.. 



Автор: fuckingniger 3.11.2010, 14:52
после добавления виртуального наследования меня изменилось следующее: 

Код

class worker_procent_of_sum:virtual public worker


Код

class worker_with_base:virtual public worker


это прямые предки worker больше ничего не изменял

Цитата(mes @  3.11.2010,  14:46 Найти цитируемый пост)
добавьте в список инициализации конструктора 4го класса инициализацию виртуального класса.. 

как это делается??

Автор: mes 3.11.2010, 14:56
также как прямые предки инициализируете, так и косвенные виртуальные предки.. 

Автор: fuckingniger 3.11.2010, 14:56
после добавления виртуального наследования меня изменилось следующее: 

Код

class worker_procent_of_sum:virtual public worker


Код

class worker_with_base:virtual public worker


это прямые предки worker больше ничего не изменял

Цитата(mes @  3.11.2010,  14:46 Найти цитируемый пост)
добавьте в список инициализации конструктора 4го класса инициализацию виртуального класса.. 

как это делается??

Автор: mes 3.11.2010, 15:01
Цитата(mes @  3.11.2010,  13:56 Найти цитируемый пост)
также как прямые предки инициализируете, так и косвенные виртуальные предки.. 


Автор: fuckingniger 3.11.2010, 15:08
mes - извиняюсь что-то и-нет  тормозил просто 


да....заработало

а в википедии такого не было)

то есть если используешь виртуальное наследование - то надо явно вызывать конструкторы всех предков  до точки начала этого наследования ??? я просто думал что они и так слишком часто у меня вызывались 

Автор: mes 3.11.2010, 15:18
Цитата(fuckingniger @  3.11.2010,  14:08 Найти цитируемый пост)
то надо явно вызывать конструкторы всех предков  до точки начала этого наследования ??? 

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

Добавлено через 1 минуту и 6 секунд
Цитата(fuckingniger @  3.11.2010,  14:08 Найти цитируемый пост)
а в википедии такого не было)

заведите себе более надежный учебник, чем вики.. ее можно использовать лишь для внешнего ознакомления..

Автор: fuckingniger 3.11.2010, 15:51
mes

это правда очень заметно повлияло на производительность .....у меня программа секунд 10 запускается 

Автор: mes 3.11.2010, 16:03
Цитата(fuckingniger @  3.11.2010,  14:51 Найти цитируемый пост)
Это правда очень заметно повлияло на производительность .....у меня программа секунд 10 запускается  

правда ?!  smile чего это Вы там такого понаписали  smile 
но однозначно, что повлияло не "это" smile

Автор: fuckingniger 3.11.2010, 16:04
видимо пора перзагрузиться)))

Автор: fuckingniger 3.11.2010, 19:02
меня клинит сегодня)))
не  надо запускать проект с флешки просто

Автор: Master01 3.11.2010, 19:15
Цитата

Master01
пробовал так  - не работает у меня


странно, вот этот код у меня работает нормально (VS2008 Express)

Может я что-то не так понял ....

Код

class A
{
public:
    A() {}
};

class B
    : public A
{
public:
    B() : A() {}
};

class C
    : public A
{
public:
    C() : A() {}
};

class D
    : public B, public C
{
public:
    D() : B(), C() {}
};



void main()
{
    D* pd = new D();

    A* pa = (A*)static_cast<C*>(pd);
}

Автор: fuckingniger 3.11.2010, 19:16
Master01
не уверен но кажется что если конструкторы добавить то упадёт ..ну или у меня руки не оттуда растут что вполне вероятно

Добавлено через 5 минут и 26 секунд
ещё одна проблема(. Сделал класс 1 абстрактным (по заданию надо)  теперь у меня 3 неразрешимых внешних символа в классе 3 и 4 . даже не знаю что вам выложить чтоб стало понятно ...
вот ошибки :
 
Код

error LNK2019: unresolved external symbol "public: virtual double __thiscall worker_procent_of_sum::salary(void)" (?salary@worker_procent_of_sum@@UAENXZ) referenced in function "public: virtual double __thiscall worker_percent_plus_base::salary(void)" (?salary@worker_percent_plus_base@@UAENXZ)

error LNK2019: unresolved external symbol "public: virtual void __thiscall worker_procent_of_sum::print(void)" (?print@worker_procent_of_sum@@UAEXXZ) referenced in function "public: virtual void __thiscall worker_percent_plus_base::print(void)" (?print@worker_percent_plus_base@@UAEXXZ)

error LNK2019: unresolved external symbol "public: virtual void __thiscall worker::print(void)" (?print@worker@@UAEXXZ) referenced in function "public: virtual void __thiscall worker_with_base::print(void)" (?print@worker_with_base@@UAEXXZ)



вот весь проект :
http://rghost.ru/3135108

Автор: fuckingniger 3.11.2010, 19:38
теперь не разрешает через :: обращаться к одноименным методам предков ...

Добавлено @ 19:51
или не разрешает обращаться к виртуальным функциям предков   через ::

Добавлено @ 19:52
но мне надо сделать так : виртуальная функция потомка состоит частично из функции предка + ещё некоторый код 

в книге ща нашёл - именно так и делается а у мну косяки (

Автор: fuckingniger 3.11.2010, 20:40
((

Автор: fuckingniger 3.11.2010, 21:24
mes

чувствую, что вся надежда на тебя !!!

Автор: mes 3.11.2010, 21:51
Цитата(fuckingniger @  3.11.2010,  18:38 Найти цитируемый пост)
теперь не разрешает через ::

покажите пример использования.. ( в прикрепленном проекте искать лень )

Цитата

теперь у меня 3 неразрешимых

добавьте определение соответствующих функций, а если есть, то проверьте сигнатуры на несовпадение.. 


Автор: Леопольд 3.11.2010, 22:37
fuckingniger, мне кажется что ты неправильно понимаешь для чего нужно public наследование. Не для того что-bы повторно использовать bазовый класс и его данные, а наоbорот, что-bы использовать наследника через унифицированный интерфейс bазового класса. Оbычно в роли интерфесов использются аbстрактные классы, которые "заставляют" переопределить чистые виртуальные функции у наследника. Как правило, при таком подходе подоbное (двойное) наследование не нужно.

P.S. оbычно, каст от bазового к наследнику говорит о неправильном проектировании.

Автор: mes 3.11.2010, 22:49
Цитата(Леопольд @  3.11.2010,  21:37 Найти цитируемый пост)
нужно public наследование. Не для того что-bы повторно использовать bазовый класс и его данные, а наоbорот, что-bы использовать наследника через унифицированный интерфейс bазового класса. 

при чем тут паблик-наследование ?!  smile 

Автор: Леопольд 3.11.2010, 23:46
Цитата(mes @  3.11.2010,  22:49 Найти цитируемый пост)
при чем тут паблик-наследование ?!
При том что только public позволяет кастовать к bазовому типу. При этом неявно. В тоже время, это не пользовательский каст, т.е. в цепочке таких кастов может bыть bольше одного.
Код
struct Base
{
    operator int()
    {
        return 0;
    }
};

struct Child: public Base{};

void bar(int) {}

int main()
{
    Child child;

    bar(child);

    return 0;
}


protected и private наследование не позовляют делать такой каст, даже явный, так что использовать указатель на bазовый класс для раbоты с виртуальными функциями не выйдет. 
Такое наследование скорее bлиже к агрегации по смыслу.

P.S. новички часто допускают ошиbку в виде невиртуального деструктора в bазовом классе, при этом делая delete на него. Если класс предназначен для наследования интерфейса то нужно или определить protected деструктор или виртуальный public деструктор.

Если не читал, то стоит почитать "стандарты программирования на С++", Александреску и Саттер. Квинтэессенция знаний в виде простых правил.

Автор: fuckingniger 4.11.2010, 01:13
в классе percent_plus_base (самый последний наследник )
ругается на print как и во все остальных впрочем 

вот пример в этом классе 
Код

void worker_percent_plus_base::print()
{
    worker_procent_of_sum::print();
    cout << "\t\t\t\t\tand his base " << base << endl;
    cout << "\t\t\t\t\tgeneral salary " <<salary() << endl;
    full_salary=salary();
}


ругается также  в  worker_procent_of_sum.obj как он пишет и также на print 
Код

void worker_procent_of_sum::print()
{
    worker::print();
        cout <<"\t\t\t\t\tand his sum of sale  " <<sum_all<<endl<<
        "\t\t\t\t\tpercent of sale " << percent<<endl<<
        "\t\t\t\t\t his salary "<<salary()<<endl;
}



а вот в worker_with_base и то же самое 
Код

# ifndef WORKER_WITH_BASE
# define WORKER_WITH_BASE
# include "worker.h"
class worker_with_base:virtual public worker
{
public:
    worker_with_base(string f,string l,int days,company& company_1, double fix);
    void set_base(double fix);
    double get_base();
     virtual void print ();
    virtual double salary();
protected:
    double base;
};

#endif


Добавлено через 8 минут и 31 секунду
MES !!!!

Автор: fuckingniger 4.11.2010, 01:43
в хедерах объявлено что print просто виртуальная функция (начиная с класса worker - абстрактного (чисто виртуальная функция там = salary )).....что не нравится - не могу понять 

Автор: mes 4.11.2010, 01:45
Цитата(fuckingniger @  4.11.2010,  00:13 Найти цитируемый пост)
ругается на print как и во все остальных впрочем 

ну неужто трудно скопировать сообщение компилятора ?
из за его отсутствия придется код пересматривать..

Добавлено через 1 минуту и 14 секунд
Цитата(fuckingniger @  4.11.2010,  00:13 Найти цитируемый пост)
MES !!!! 

это типа прикрик, почему я до сих пор не занялся Вашей темой ?  smile  или я неправильно понял smile

Автор: fuckingniger 4.11.2010, 01:51
Цитата(mes @  4.11.2010,  01:45 Найти цитируемый пост)
это типа прикрик, почему я до сих пор не занялся Вашей темой ?    или я неправильно понял 

это огромная надежда лично на ВАС!!! 
сообщения компилятора скопированы чуть выше ....но вот ещё раз -правда чутка поменялись......
Код

1>worker_percent_plus_base.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall worker::print(void)" (?print@worker@@UAEXXZ)
1>worker_procent_of_sum.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall worker::print(void)" (?print@worker@@UAEXXZ)
1>worker_with_base.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall worker::print(void)" (?print@worker@@UAEXXZ)

Автор: mes 4.11.2010, 01:55
Цитата(fuckingniger @  4.11.2010,  00:51 Найти цитируемый пост)
сообщения компилятора скопированы чуть выше ....но вот ещё раз -правда чутка поменялись......

так на эти сообщения уже был ответ.. о том что нет тела функции названных методов..

Добавлено через 2 минуты и 27 секунд
а почему не находит, смотрите внимательно.. или к проекту не все файлы подключены он не компилит
или просто ребилд надо сделать.. или где то разница в написании - не узнает.. 

Автор: fuckingniger 4.11.2010, 01:59
ну понимаете просто пока не завиртуалил функции - всё работало ...так то тела есть и написаны правильно ...

посмотрите пожалуйста опытным взглядом

Добавлено через 50 секунд
завиртуалил (абсолютно ) salary 

и print просто 

Автор: mes 4.11.2010, 02:02
Цитата(fuckingniger @  4.11.2010,  00:59 Найти цитируемый пост)
абсолютно 

=0 показывает компилятору, что тела не должно быть.. если Вы этого не поставите, то обязаны написать тело.. 

Автор: fuckingniger 4.11.2010, 02:05
mes - всё так и есть 
хедер 
Код

#ifndef WORKER_H
#define WORKER_H
# include <string>
using namespace std;
class company;
struct name
{
    string first_name;
    string last_name;
};
class worker
{
protected:
    string first_name;
    string last_name;
    int time_of_work;
public:
    worker(string s1, string s2 , int time,company& company_1);
    virtual string get_first_name();
    virtual string get_last_name();
    virtual void print();
    virtual double salary()=0;
};
#endif

реализация 
Код

# include <iostream>
#include "company.h"
# include "worker.h"


worker::worker(string s1, string s2 , int time,company& head_company):first_name(s1), last_name(s2), time_of_work(time) 
    {
        head_company.add_in_list(s1,s2);
    }
string worker::get_first_name()
    {
        return first_name;
    }
string worker::get_last_name()
    {
        return last_name;
    }
void worker::print()
    {    
        cout <<"\t\t\t\t\t"<<get_first_name() << " " << get_last_name()<<endl;
        cout<<"\t\t\t\t\t"<< "his experience is " << time_of_work << " days\n";
    }



Добавлено @ 02:06
пока скилла просто нет -не  понимаю вообще в чём дело даже примерно (

Добавлено через 2 минуты и 50 секунд
что - то с принтом .....ума не приложу что 

Автор: mes 4.11.2010, 02:17
Цитата(mes @  4.11.2010,  00:55 Найти цитируемый пост)
а почему не находит, смотрите внимательно.. или к проекту не все файлы подключены он не компилит
или просто ребилд надо сделать.. или где то разница в написании - не узнает.. 

ну разницы на вид нет..а остальные два пункта проверили ?

Автор: fuckingniger 4.11.2010, 02:20
worker.h
Код

#ifndef WORKER_H
#define WORKER_H
# include <string>
using namespace std;
class company;
struct name
{
    string first_name;
    string last_name;
};
class worker
{
protected:
    string first_name;
    string last_name;
    int time_of_work;
public:
    worker(string s1, string s2 , int time,company& company_1);
    virtual string get_first_name();
    virtual string get_last_name();
    virtual void print();
    virtual double salary()=0;
};
#endif

worker.cpp
Код

# include <iostream>
#include "company.h"
# include "worker.h"


worker::worker(string s1, string s2 , int time,company& head_company):first_name(s1), last_name(s2), time_of_work(time) 
    {
        head_company.add_in_list(s1,s2);
    }
string worker::get_first_name()
    {
        return first_name;
    }
string worker::get_last_name()
    {
        return last_name;
    }
void worker::print()
    {    
        cout <<"\t\t\t\t\t"<<get_first_name() << " " << get_last_name()<<endl;
        cout<<"\t\t\t\t\t"<< "his experience is " << time_of_work << " days\n";
    }


worker_with_base.h
Код

# ifndef WORKER_WITH_BASE
# define WORKER_WITH_BASE
# include "worker.h"
class worker_with_base:virtual public worker
{
public:
    worker_with_base(string f,string l,int days,company& company_1, double fix);
    void set_base(double fix);
    double get_base();
     virtual void print ();
    virtual double salary();
protected:
    double base;
};

#endif

worker_with_base.cpp
Код

#include "worker_with_base.h"
# include "worker.h"
# include <iostream>
using namespace std;

worker_with_base::worker_with_base(string f,string l,int days,company& company_1, double fix):worker(f,l,days,company_1),base(fix)
{}
    void worker_with_base::set_base(double fix)
    {
        base=fix;
    }
    double worker_with_base::get_base()
    {
        return base;
    }
    void worker_with_base::print ()
    {
        worker::print();
        cout << "\t\t\t\t\tand have base salary " << get_base() <<  endl;
    }
    double worker_with_base::salary()
    {
        return get_base();
    }

worker_procent_of_sum.h
Код

#ifndef WORKER_PROCENT_OF_SUM 
#define WORKER_PROCENT_OF_SUM 

# include "worker.h"
# include <iostream>
using namespace std;
class worker_procent_of_sum:virtual public worker
{
public:
    worker_procent_of_sum(string s1,string s2,int d,company& company_1,double sum,double prc) ;
    void set_sum_all(double);
    void set_percent(double);
    virtual double salary();
    virtual void print ();
protected:
    double sum_all;
    double percent;
};
#endif

worker_procent_of_sum.cpp
Код

# include "worker.h"
#include "worker_procent_of_sum.h"
using namespace std;
//конструктор
worker_procent_of_sum::worker_procent_of_sum(string s1,string s2,int d,company& company_1,double sum,double prc)
:worker(s1,s2,d,company_1),sum_all(sum),percent(prc)
{}

void worker_procent_of_sum::set_percent(double perc)
{
    percent=perc;
}
void worker_procent_of_sum::set_sum_all(double s)
{
    sum_all=s;
}
double worker_procent_of_sum::salary()
{
    return (percent/100)*sum_all;
}
void worker_procent_of_sum::print()
{
    worker::print();
        cout <<"\t\t\t\t\tand his sum of sale  " <<sum_all<<endl<<
        "\t\t\t\t\tpercent of sale " << percent<<endl<<
        "\t\t\t\t\t his salary "<<salary()<<endl;
}

worker_procent_plus_base.h
Код

# ifndef WORKER_PERCENT_PLUS_BASE
# define WORKER_PERCENT_PLUS_BASE

# include "worker_procent_of_sum.h"
# include "worker_with_base.h"
# include <iostream>
using namespace std;

class worker_percent_plus_base: public worker_procent_of_sum, public worker_with_base
{
    friend ostream& operator <<(ostream&,  worker_percent_plus_base&);
protected :
    double full_salary;
public :
    worker_percent_plus_base(string first,string las,int days,company& company_1,double fix ,double sum_of_sale, double procent);
    double salary();
    void print();
    worker_percent_plus_base& operator++();//увеличение процента на 5 префиксна форма
    worker_percent_plus_base operator++(int);//постфикс база увеличивается на 10 %
};

# endif

worker_procent_plus_base.cpp
Код

# include "worker_percent_plus_base.h"


worker_percent_plus_base::worker_percent_plus_base(string first,string las,int days,company& head_company,double fix ,double sum_of_sale, double procent)
:worker_procent_of_sum(first,las,days,head_company,sum_of_sale,procent),worker_with_base(first,las,days,head_company,fix),worker(first,las,days,head_company)//base(fix)
{
    full_salary=salary();
}

double worker_percent_plus_base::salary()
{
    return (get_base()+worker_procent_of_sum::salary());
}
void worker_percent_plus_base::print()
{
    worker_procent_of_sum::print();
    cout << "\t\t\t\t\tand his base " << base << endl;
    cout << "\t\t\t\t\tgeneral salary " <<salary() << endl;
    full_salary=salary();
}
worker_percent_plus_base& worker_percent_plus_base::operator ++()//+5% префикс
{
    set_percent(percent+5);
    return *this;
}

worker_percent_plus_base worker_percent_plus_base::operator++(int)//постфикс база увеличивается на 10 %
{
    worker_percent_plus_base new_worker=*this; 
    set_sum_all(1.1*sum_all);
    return new_worker;
}
ostream& operator<<(ostream& output,worker_percent_plus_base& w_p_p_b)
{
    output <<" "<<w_p_p_b.first_name <<" "<< w_p_p_b.last_name<<
        "\nsalary:"<<w_p_p_b.salary()<<endl;
    return output;
}

и не особо относящееся к делу 

company.h
Код

#ifndef COMPANY_H
#define COMPANY_H
# include "worker.h"
# include <string>
using namespace std;
class company 
{
private :
    bool search(string s1,string s2);
    name employees_list[1000];
    int number_employee;
public :
    company();
    void add_in_list(string first , string  last );
    //void add_in_list( worker w_1 );
    int  get_number_employee();
    void print ();
};

#endif

company.cpp
Код

# include <iostream>
# include <string>
# include "company.h"
using namespace std;

    company::company():number_employee(0)
    {}
    void company::add_in_list(string first , string  last )
    {
        if(!search(first,last))
        {
            employees_list[number_employee].first_name=first;
            employees_list[number_employee++].last_name=last;
        }
    }
    /*void company::add_in_list( worker w_1 )
    {
        if(!search( w_1.get_first_name() , w_1.get_last_name() ))
        {
            employees_list[number_employee].first_name=w_1.get_first_name();
            employees_list[number_employee++].last_name=w_1.get_last_name();
        }
    }*/
    int  company::get_number_employee()
    {
        return number_employee;
    }
    void company::print ()
    {
        cout << "\t\tat this company works " <<get_number_employee() <<" persons:\n";
        for (int i=0;i<number_employee; i++)
            cout <<"\t\t\t" <<employees_list[i].first_name<< " "<< employees_list[i].last_name<<endl; 
        cout << endl;
    }
    bool company::search(string s1,string s2)
    {
        for (int i=0;i<number_employee;i++)
        {
            if(s1==employees_list[i].first_name && s2==employees_list[i].last_name )
                return true;
        }
        return false;
    }


Добавлено через 4 минуты и 14 секунд
компилил и линковал заново - кучу раз 
причём причём при компиляции не ошибок - все на линковке - подключено вроде бы всё - как я себе представляю(оно и до этого всё было подклюдчено)

Добавлено через 4 минуты и 54 секунды
и забыл main вот 
Код

# include <iostream>
# include <vector>
# include <string>
# include "company.h"
# include "worker_percent_plus_base.h"
using namespace std;
void input_worker_with_base(string &f,string &l,int &days, double& fix);
void input_worker_procent_of_sum(string &f,string &l,int &days, double& sum,double& prc);
void input_worker_percent_plus_base(string &f,string &l,int &days, double& fix, double& sum,double& prc);
int  search(string s1,string s2,vector<worker*> workers,int index, int& index_in_workers);
int main ()
{
    company head_company;
    string s1,s2;
    int index_in_workers,type;
    int time,k,number_workers, index =0;
    double base , salary , procent ,sum_of_sale;
    bool is_end=false;
    worker_with_base* ptr_1;
    worker_procent_of_sum* ptr_2;
    worker_percent_plus_base* ptr_3;
    
    cout << "input number workers in company >";cin>>number_workers;
    vector <worker*>workers(number_workers); 
    do {
        cout << "\ninput number\n\t1-add new worker"
                "\n\t2-number workers "
                "\n\t3-watch worker_list "
                "\n\t4-go to worker... "
                "\n\t9-exit "
                "\n\t>";cin>>k;
        switch(k)//главный
        {
        case 1:
            if (number_workers==index)
            {
                cout <<"error!!! State is full !!!"<<endl ;
                break;
            }
            cout << "\t\twhat type of worker?\n\t\t1-base\n\t\t2-procent\n\t\t3-base+procent\n\t\t>";cin>>k;
            switch(k)//на добавление какого типа работника
            {
            case 1:
                input_worker_with_base(s1,s2,time,base);
                workers[index++]=new worker_with_base(s1,s2,time,head_company,base);
                break;
            case 2:
                input_worker_procent_of_sum(s1,s2,time,sum_of_sale,procent);
                workers[index++]=new worker_procent_of_sum(s1,s2,time,head_company,sum_of_sale,procent);
                break;
            case 3:
                input_worker_percent_plus_base(s1,s2,time,base,sum_of_sale,procent);
            //    dynamic_cast<worker_percent_plus_base*>(workers[index]);
                workers[index++]=new worker_percent_plus_base(s1,s2,time,head_company,base,sum_of_sale,procent);
                break;
            }
            cout << "Worker add succes !!!"<<endl;
            if (number_workers==index)
                cout <<"state is full !!!"<<endl ; 
            break;
        case 2:
            cout << "\t\t>"<<head_company.get_number_employee();
            break;
        case 3:
            head_company.print();
            break;
        case 4:
            cout << "\t\tinput first name:";cin >> s1;
            cout << "\t\tinput last name:";cin >> s2;
            type=search(s1,s2,workers,index,index_in_workers);
            switch(type)//в зависимости от типа работника предлагаем функции 
            {
            case 1://работник worker with base
                ptr_1 = dynamic_cast<worker_with_base*> (workers[index_in_workers]);
                cout << "\t\t\tinput number:\n"
                        "\t\t\t\t 1-get name\n"
                        "\t\t\t\t 2-set base\n"
                        "\t\t\t\t 3-get base\n"
                        "\t\t\t\t 4-print about worker\n\t\t\t\t>";
                        cin >> k;
                        switch(k)
                        {
                        case 1:
                            cout <<"\t\t\t\t\t>"<<ptr_1->get_first_name()<<" "<<ptr_1->get_last_name()<<endl;        
                            break;
                        case 2:
                            cout <<"\t\t\t\t\t"<<"input base >";cin >> base;
                            ptr_1->set_base(base);
                            cout << "Succes set base!";
                            break;
                        case 3:
                            cout <<"\t\t\t\t\t>"<<ptr_1->get_base();
                            break;
                        case 4:
                            ptr_1->print();
                            break;
                        }
                break;
            case 2:
                ptr_2 = dynamic_cast<worker_procent_of_sum*> (workers[index_in_workers]);
                cout << "\t\t\tinput number:\n"
                    "\t\t\t\t 1-get name\n"
                    "\t\t\t\t 2-set sum of sales\n"
                    "\t\t\t\t 3-set percent \n"
                    "\t\t\t\t 4-get salary \n"
                    "\t\t\t\t 5-print about worker\n\t\t\t\t>";
                cin >> k;
                switch(k)
                {
                case 1:
                    cout <<"\t\t\t\t\t>"<<ptr_2->get_first_name()<<" "<<ptr_2->get_last_name()<<endl;        
                    break;
                case 2:
                    cout <<"\t\t\t\t\t"<<"input sum of sales >";cin >> sum_of_sale;
                    ptr_2->set_sum_all(sum_of_sale);
                    cout << "Succes set sum of sales!";
                    break;
                case 3:
                    cout <<"\t\t\t\t\t"<<"input percent >";cin >> procent;
                    ptr_2->set_percent(procent);
                    cout << "Succes set percent!";
                    break;
                case 4:
                    cout<<"\t\t\t\t\t"<<ptr_2->salary();
                    break;
                case 5:
                    ptr_2->print();
                    break;
                }
                break;
            case 3:
                ptr_3 = dynamic_cast<worker_percent_plus_base*> (workers[index_in_workers]);
                cout << "\t\t\tinput number:\n"
                    "\t\t\t\t 1-get name\n"
                    "\t\t\t\t 2-set sum of sales\n"
                    "\t\t\t\t 3-set percent \n"
                    "\t\t\t\t 4-get salary \n"
                    "\t\t\t\t 5-set base\n"
                    "\t\t\t\t 6-get base\n"
                    "\t\t\t\t 7-print about worker\n\t\t\t\t>";
                cin >> k;
                switch(k)
                {
                case 1:
                    cout <<"\t\t\t\t\t>"<<ptr_3->get_first_name()<<" "<<ptr_3->get_last_name()<<endl;        
                    break;
                case 2:
                    cout <<"\t\t\t\t\t"<<"input sum of sales >";cin >> sum_of_sale;
                    ptr_3->set_sum_all(sum_of_sale);
                    cout << "Succes set sum of sales!";
                    break;
                case 3:
                    cout <<"\t\t\t\t\t"<<"input percent >";cin >> procent;
                    ptr_3->set_percent(procent);
                    cout << "Succes set percent!";
                    break;
                case 4:
                    cout<<"\t\t\t\t\t"<<ptr_3->salary();
                    break;
                case 5:
                    cout <<"\t\t\t\t\t"<<"input base >";cin >> base;
                    ptr_3->set_base(base);
                    cout << "Succes set base!";
                    break;
                case 6:
                    cout <<"\t\t\t\t\t>"<<ptr_3->get_base();
                    break;
                case 7:
                    ptr_3->print();
                    break;
                }
                break;
            }
            break;
        case 9:
            is_end=true;
            break;
        }        
    }while(!is_end );
    return 0;
}
void input_worker_with_base(string &f,string &l,int &days, double& fix)
{
    cout << "\t\t\tinput first name > ";cin >> f;
    cout << "\t\t\tinput last name > ";cin >> l;
    cout << "\t\t\tinput experience(days)> ";cin >> days;
    cout << "\t\t\tinput base salary > ";cin >> fix;
}
void input_worker_procent_of_sum(string &f,string &l,int &days, double& sum,double& prc)
{
    cout << "\t\t\tinput first name > ";cin >> f;
    cout << "\t\t\tinput last name > ";cin >> l;
    cout << "\t\t\tinput experience(days) > ";cin >> days;
    cout << "\t\t\tinput sum of sale > ";cin >> sum;
    cout << "\t\t\tinput procent of sale > ";cin >> prc;
}
void input_worker_percent_plus_base(string &f,string &l,int &days, double& fix, double& sum,double& prc)
{
    input_worker_procent_of_sum(f,l,days,sum,prc);
    cout << "\t\t\tinput base salary > ";cin >> fix;
}

int search(string s1,string s2,vector<worker*> workers,int index,int& index_in_workers)
{
    index_in_workers=-1;
    for (int i=0;i<index;i++)
        if(s1==workers[i]->get_first_name() && s2==workers[i]->get_last_name())
        {
            index_in_workers=i;
        }
    if(index_in_workers==-1)
    {
        cout <<"\t\t\tthis worker not found!";
        return -1;
    }
    else
    {
        worker_with_base* ptr_1;
        worker_procent_of_sum* ptr_2;
        worker_percent_plus_base* ptr_3;
        ptr_1=dynamic_cast<worker_with_base*>(workers[index_in_workers]);
        ptr_3=dynamic_cast<worker_percent_plus_base*>(workers[index_in_workers]);
        if(ptr_3!=0)
        {
            return 3;
        }
        if(ptr_1!=0)
        {
            return 1;
        }
        ptr_2=dynamic_cast<worker_procent_of_sum*>(workers[index_in_workers]);
        if(ptr_2!=0)
        {
            return 2;
        }
        return -2;
    }
}

Автор: mes 4.11.2010, 02:25
вес- код можно (желательно) было не выкладывать, а только те куски что имееют значение... 
т.е. где непосредственно фигурируерт принт..  smile

Добавлено через 29 секунд
Цитата(mes @  4.11.2010,  01:17 Найти цитируемый пост)
Цитата

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


ну разницы на вид нет..а остальные два пункта проверили ? 


Автор: fuckingniger 4.11.2010, 02:34
print фигурирует везде собственно а ругается на него в 
percent_plus_base
предположительно строка - 16
worker_procent_of_sum
предположительно строка - 23
worker_with_base
предположительно строка - 18

в общем везде где переопределяется print

Добавлено через 4 минуты и 35 секунд
какие 2 пункта ?
f7  и ctrll+f7 нажимал...
всё вроде присоединено (в ресурсах и сурсах всё есть )

Автор: mes 4.11.2010, 02:40
Цитата(fuckingniger @  4.11.2010,  01:34 Найти цитируемый пост)
в общем везде где переопределяется print 

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

форумчане не видят что у Вас там происходит, что Вы делаете, и что планируете... 

итак 

Цитата

остальные два пункта проверили ? 

Автор: fuckingniger 4.11.2010, 02:43
ничего особого не делал - тупо везде подключал много раз всё подряд - результата 0

Добавлено через 11 секунд
про 2 пункта чуть выше

Автор: mes 4.11.2010, 02:50
Цитата(fuckingniger @  4.11.2010,  01:34 Найти цитируемый пост)
f7  и ctrll+f7 нажимал...

 smile  smile  smile 
ну вот опять.. я ведь не знаю какая у Вас иде , и к чему приаттачены эти клавиши...
smile 
чтоб убедиться, что у вас иде все правильно сделалал,  сотрите вручную в целевой папке все .obj и .exe
и снова скомпилите ..




Автор: fuckingniger 4.11.2010, 02:55
mes ты просто гений !!!

я думал раньще - такое невозможно!!!!

это глюк компилятора????

Автор: mes 4.11.2010, 02:56
Цитата(fuckingniger @  4.11.2010,  01:55 Найти цитируемый пост)

это глюк компилятора???? 

скорее глюк ИДЕ..

Добавлено через 1 минуту и 14 секунд
если, конечно, Вы правильные клавиши нажимали.. smile

Автор: fuckingniger 4.11.2010, 03:08
я и мышко тоже щелкал))))

а что такое иде???раскладка???

Автор: mes 4.11.2010, 09:38
Цитата(fuckingniger @  4.11.2010,  02:08 Найти цитируемый пост)
а что такое иде???раскладка??? 

нет..
http://forum.vingrad.ru/forum/topic-270683/kw-compiler-linker-preprocessor.html

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)