Различия между объектной моделью C++ и Java [закрыто]

  1. в Java очень легко сериализовать объекты. В C++ это только безопасно(?) to memcpy объекты, если они похожи на структуры C (без полиморпизма). В C++, если компилятор может генерировать конструктор копирования по умолчанию (тривиальный), то почему он не может генерировать код для автоматической сериализации?

  2. в Java только статические функции и члены данных доступны из ctor. В C++ я могу с удовольствием использовать нестатический члены и функции из конструктора.

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

  4. в Java я могу инициализировать final члены в ctor. В C++ я должен сделать инициализацию const члены в списке инициализации. в C++, когда элемент управления достигает тела ctor, все члены ctor запускаются, верно?

  5. В Java конструктор можно вызвать другой конструктор. В C++ мы не можем этого сделать.

  6. в Java this недопустимо до тех пор, пока ctor не вернется (побег this ссылка, ошибка в многопоточности). Когда this действительно на C++? The this можно легко избежать как в C++, так и в Java:регистрация еще не построенного объекта для слушателей в ctor (шаблон Observer).

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

может ли кто-нибудь дать краткое объяснение этим различиям?

обновление. пытаясь собрать ответы, полученные до сих пор.

  1. Boost имеет некоторую поддержку сериализации. (Тони)

  2. несмотря на то, что я испортил этот момент, Альф П. Штайнбах дал интересный образец.

  3. C++0x будет поддерживать гораздо более практическую инициализацию, чем C++98. (Alf P. Steinbach) #3 будет законным в C++0x (Ken Bloom)

  4. члены данных, объявленные в собственном классе конструктора, гарантированно будут полностью построены к моменту запуска {body} конструктора. (c++-faq-lite)

  5. C++0x позволит конструкторам вызывать другие одноранговые конструкторы (Википедия, В C++0х)

  6. C++03 рассматривает объект, который будет построен, когда его конструктор завершит выполнение (Википедия).

  7. такие вещи, как управление доступом, имеют мало общего с объектной моделью: это особенность системы управления доступом, которая является функцией времени компиляции. (Yttrill)

6 ответов


в Java очень легко сериализовать объекты. В C++ это только безопасно(?) в функции memcpy объекты так долго, как они похожи с структуры (не polymorpism).

Java-это интерпретируемый язык (или совсем недавно, как комментирует Билли, JIT compiled), поэтому у него нет выбора, кроме как носить багаж метаданных каждого типа данных в программе во время выполнения. Между интерпретатором, виртуальной машиной, дополнительным компилятором и накладными расходами метаданных Java-программам требуется много памяти. C++ является скомпилированный язык, где многие решения Java принимаются один раз во время компиляции, а метаданные не используются для интерпретации, чтобы направлять сериализацию во время выполнения. В общем, метаданные не отображаются даже во время компиляции, вероятно, потому, что разные поставщики компиляторов моделируют программу совершенно по-разному, и они не согласовали подходящее представление. Это также значительная работа. У Бьярне Страуструпа есть некоторые документы о том, как раскрывать такую информацию, но это не так даже запланированный для C++0x. Между тем, с небольшой ручной разметкой программы C++ могут сериализовать объекты - см. boost для хорошей реализации.

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

каждый конструктор C++ предоставляет полное независимое представление о том, как будет инициализирован объект. Если стоит, общие шаги строительства могут быть учтены в рутине поддержки, но призыв к этому по-прежнему отображается в конструкторе. Имея, чтобы проверить различные задания разбросаны по класс delocalise, что, хотя он, безусловно, могут быть удобным. Я бы рискнул предположить, что здесь многое.

в Java я могу инициализировать конечные члены в ctor. В C++ я должен выполнить инициализацию членов const в списке инициализации.

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

в Java ctor может вызвать другой ctor. В C++ мы не можем этого сделать.

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

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

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

может ли кто-нибудь дать краткое объяснение этим различиям?

Ну, я пытался.


многие из них охвачены философией дизайна.

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

В C++ они обычно предполагают, что программист знает все потенциальные последствия его действий и позволяет им делать все, что они хотят.

также Java отбросил обратную совместимость C, вы не можете взять файл C и передать его компилятору Java.

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


Это разные языки. - как это для краткости.


название операции немного запутано. Такие вещи, как управление доступом, имеют мало общего с объектной моделью: это особенность системы управления доступом, которая является функцией времени компиляции. Последовательность построения объекта is часть объектной модели. Ключевое различие между C++ и Java заключается в том, что Java-это собранный мусор, а C++ - нет.

другое основное различие заключается в том, что Java использует архаичный crud для полиморфизма, тогда как C++ использует сломанный концепция шаблона: в шаблонах C++ время компиляции и не влияют на объектную модель, в Java полиморфизм взломан вокруг того, что в C++ называется динамическим приведением.

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

существуют системные различия более низкого уровня: Java имеет минимальный статический тип, но в остальном является в основном динамическим языком. На C++ с другой стороны почти полностью статически типизированный язык, за исключением исключений ..:)


немного Java:

class Base
{
    public static void say( String s )
    {
        System.out.println( s );
    }

    public void sayHello()
    {
        say( "Base.sayHello says..." );
        say( "Base hello" );
    }

    public Base()
    {
        sayHello();
    }
}

class Derived
    extends Base
{
    private String[] myWords;   // = new String[]{ "Derived", "hello" };

    public void sayHello()
    {
        say( "Derived.sayHello says..." );
        say( myWords[0] + " " + myWords[1] );
    }

    Derived()
    {
        myWords = new String[]{ "Derived", "hello" };
    }
}

class ConsCallDemo
{
    public static void main( String[] args )
    {
        Derived o = new Derived();
    }
}

выход:

Derived.sayHello says...
Exception in thread "main" java.lang.NullPointerException
        at Derived.sayHello(conscall.java:28)
        at Base.(conscall.java:16)
        at Derived.(conscall.java:32)
        at ConsCallDemo.main(conscall.java:41)

проблема в том, что в Java вызовы спускаются к классу, из которого создается объект, еще до того, как этот экземпляр был инициализирован.

C++ защищает от этой проблемы, динамически настраивая тип объекта во время строительства и разрушения. В C++ объект имеет тип Base когда Base конструктор выполняет. Таким образом, звонки внутри Base конструктор ведет себя, если объект был создан из класса Base, независимо от фактического класса создания экземпляра.

пример этого, просто переводя код Java выше на C++:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

void say( string const& s )
{
    cout << s << endl;
}

class Base
{
public:
    virtual void sayHello() const
    {
        say( "Base::sayHello says..." );
        say( "Base hello" );
    }

    Base()
    {
        sayHello();
    }
};

class Derived
    : Base
{
private:
    vector< string >    words_;

public:
    void sayHello() const
    {
        say( "Derived::sayHello says..." );
        say( words_[0] + " " + words_[1] );
    }

    Derived()
    {
        words_.push_back( "Derived" );
        words_.push_back( "hello" );
    }
};

int main()
{
    Derived o;
}

выход:

Base::sayHello says...
Base hello

один из способов получить вероятно предназначены эффект на C++, который также предлагает, как это сделать (правильно) на Java:

#include <iostream>
#include <string>
#include <vector>
#include <boost/shared_ptr.hpp>
using namespace std;

void say( string const& s )
{
    cout << s << endl;
}

class Base
{
protected:
    struct Greeter
    {
        typedef boost::shared_ptr< Greeter >    Ptr;

        virtual void sayHello() const
        {
            say( "Base::sayHello says..." );
            say( "Base hello" );
        }
    };

private:
    Greeter::Ptr    greeter_;

public:
    void sayHello() const
    {
        greeter_->sayHello();
    }

    Base( Greeter::Ptr greeter = Greeter::Ptr( new Greeter ) )
        : greeter_( greeter )
    {
        sayHello();
    }
};

class Derived
    : Base
{
protected:
    struct Greeter
        : Base::Greeter
    {
        vector< string >    words_;

        virtual void sayHello() const
        {
            say( "Derived::sayHello says..." );
            say( words_[0] + " " + words_[1] );
        }

        Greeter()
        {
            words_.push_back( "Derived" );
            words_.push_back( "hello" );
        }
    };

public:
    Derived()
        : Base( Greeter::Ptr( new Greeter ) )
    {}
};

int main()
{
    Derived o;
}

выход:

Derived::sayHello says...
Derived hello

Что касается ваших других моментов, это слишком много обсуждать здесь. Просто имейте в виду, что C++0x будет поддерживать гораздо более практическую инициализацию, чем C++98. А также, что формальная сторона вашего пункта 7 немного оспаривается, есть отчет о дефекте об этом (это не было разрешено в предстандартном C++, как описано в Аннотированном справочном руководстве, но, по-видимому, это должно было быть разрешено в стандартном C98, однако формулировка немного противоречива).

Cheers & hth.,


около 7:

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

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

Он будет работать со статическими методами, но я не думаю, что переопределение статических методов является хорошей практикой.

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