2015-07-01 4 views
3

Я пытаюсь использовать чисто виртуальный класс в качестве параметра в программе, однако, я получаю ошибку компиляции:C++ vs. C# - Использование интерфейсов/чистые виртуальные классы

Error 1 error C2259: 'Person': cannot instantiate abstract class

Я думаю, ошибка im get - это потому, что A) не удается создать экземпляр абстрактного класса, и B) я не могу использовать абстрактный класс, так как я бы использовал интерфейс в C#

Программа C#, приведенная ниже, иллюстрирует, что я пытаюсь сделать в C++. Как написать общий код, используя абстрактные классы в C++? если им пришлось использовать более специализированную версию Person, например. Сотрудник, код не является общим. Нужно ли использовать шаблоны?

Программа C++

#include<iostream> 
#include<vector> 

class Person { 
    public: 
     virtual std::string getName() = 0; 
     virtual void setName(std::string name) = 0; 
     virtual std::string toString() = 0; 
    private: 
     std::string name; 
}; 

class Employee : public Person { 
    public: 
     std::string getName() { 
      return this->name; 
     } 

     void setName(std::string name) { 
      this->name = name; 
     } 

    std::string toString() { 
     return "name:" + this->name; 
    } 

    private: 
     std::string name; 
}; 

class Repository { 
    public: 
     void add(Person& p) { 
      this->repo.push_back(p); 
     } 
    private: 
     std::vector<Person> repo; 
}; 

int main(int argc, char* argv[]) 
{ 
    Repository repo; 

    Employee emp1; 
    emp1.setName("John Doe"); 

    repo.add(emp1); 

    return 0; 
} 

C# Программа

interface IPerson 
{ 
    string GetName(); 
    void SetName(string name); 
    string ToString(); 
} 

class Employee : IPerson 
{ 
    private string _name; 

    public string GetName() { 
     return this._name; 
    } 

    public void SetName(string name) { 
     this._name = name; 
    } 

    public override string ToString() { 
     return "name: " + this._name; 
    } 
} 

class Repository 
{ 
    private List<IPerson> _repo; 

    public Repository() { 
     this._repo = new List<IPerson>(); 
    } 

    public void Add(IPerson p) { 
     this._repo.Add(p); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Repository repo = new Repository(); 

     Employee emp1 = new Employee(); 
     emp1.SetName("John Doe"); 

     repo.Add(emp1); 
    } 
} 
+0

В C#, помещающем 'string ToString();' в интерфейсе, по крайней мере, немного «странно», потому что все объекты, созданные 'object' (почти все) имеют перегружаемую строку' ToString(); ' – xanatos

ответ

2

Проблема заключается в том, что Repository хранит Personобъектов, и этот класс не может быть создан *. Это связано с тем, что std::vector<Person> имеет Personзначения.

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

/// class repository does not own Persons it holds 
class Repository { 
    public: 
     void add(Person& p) { 
      this->repo.push_back(&p); 
     } 
    private: 
     std::vector<Person*> repo; 
}; 

* Обратите внимание, что в общем случае можно построить объект базового класса из производного типа один. Базовый объект будет построен из базового под-объекта полученного (см. What is object slicing?). В вашем случае это не удается, потому что базовый тип является абстрактным.

+0

Вы должны объяснить, что 'repo.push_back' хочет создать обрезанную« копию »« Employee »(проблема [slocation] (https://en.wikipedia.org/wiki/Object_slicing)) – xanatos

+0

@xanatos' repo 'хранит объекты Person. Он ничего не может хранить. Я думаю, что это достаточно ясно. – juanchopanza

+0

Для вас очень ясно, что для меня совершенно ясно, что больше не программировать C++, но это не ясно для всех, кто приходит с C# или Java. Обрезка объектов не является интуитивной для этих людей, и не используется использование конструктора копирования, который происходит «за кулисами» на C++. – xanatos

1

Ваша проблема в том, что обработка памяти C++ полностью отличается от обработки памяти C#. Полностью полностью.

std::vector<> магазины экземпляры того, что вы добавляете к нему. копии - важное слово. Другая проблема заключается в том, что конструктор копирования не может быть виртуальным (см., Например, Can we make a class copy constructor virtual in C++).

Теперь ... что происходит, что когда вы делаете this->repo.push_back(p) на std::vector<> поиски в Person для конструктора копирования (потому что вы используете std::vector<Person>) и не найти, так что ошибка.

Обратите внимание, что в целом, даже если Person не абстрактный класс, ваш код будет, вероятно, все еще будет неправильно, потому что std::vector<> не будет копировать EmployeeEmployee другому, было бы скопировать Employee к нарезанных вниз Person (обычно это называется object slicing) (помните, что конструктор копирования не является виртуальным?) ...

Для решения, начните с одной заданной @juanchopanza, но помните о предложении Вы можете хранить указатели на Person вместо этого, но вы должны убедиться, что они живут по крайней мере, до тех пор, как Repository например ... это очень важное!

Аналогичный вопрос на ваш: c++: can vector<Base> contain objects of type Derived? Там принятый ответ предлагает использовать std::vector<std::unique_ptr>.

1

C# имеет типы значений (structs) и ссылочные типы (классы). C++ имеет только типы значений +, по крайней мере, три разных способа решения этого решения для языкового дизайна; ни одна из них не является бесплатной.

Boost обеспечивает сборщик мусора с плохим мусором с точки зрения shared_ptr<T>. Используя это, вы получаете семантику C# 'ish в терминах полиморфности, а несколько имен могут ссылаться на один и тот же экземпляр. Это, конечно, тоже нарушено, потому что он полагается на подсчет ссылок и RAII, поэтому он не имеет дело с круговыми зависимостями; для этого вам нужно использовать weak_ptr<T>, чтобы сломать круг. Я думаю, некоторые из этих умных указателей превратили его в недавний стандарт C++.

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

Смежные вопросы