LINUX.ORG.RU

Оператор new возвращает указатели на экземпляры разных классов в зависимости от параметров конструктора. Возможно ли?

 , , operator,


0

1

Пишу сейчас некий класс, в конструктор которого передаётся строковый параметр «тип устройства» (мотор, светодиод, уж, ёж), и в зависимости от типа устройства экземпляр класса должен себя по-разному вести. Можно ли так написать, чтобы при вызове конструктора

new Instance("LED")
я получал указатель на экземпляр класса Leds наследующего от Instance? Не хочется создавать protected переменную, в которой будет хранится переданный тип устройства, и в зависимости от её значения действовать по-разному.

Правильно я понимаю, что мне нужно перегрузить operator new? Можно увидеть пример кода?

★★★★★

фабрику напиши, епт! и вообще учи паттерны. За перегрузку new, я, боюсь, тебе руки оторвут.

JFreeM ★★★☆
()

А зачем делать это именно конструктором? Не проще ли сделать функцию

Instance* createInstance(const string& name);
dmfd
()

чуть раскрою последнее:
класс-родитель ничего не должен знать о своих потомках - одно из правил ООД.

JFreeM ★★★☆
()

Где это ты видел, что new/delete что нить знает о классе?

void *operator new(size_t size); void operator delete(void *p);

namezys ★★★★
()

Так человечество изобрело велоси^W паттерн «фабрика» :)

johnson102
()
Ответ на: комментарий от johnson102

можно конечно параметризировать new, но это опять же ничего не даст

namezys ★★★★
()

Присоединяюсь к советчикам выше: фабрика лучше этих костылей.

unfo ★★★★★
()
Ответ на: комментарий от namezys

и как это может повлиять на тип объекта?

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

struct Base
{
	Base( const string& name = "" );
	virtual void Test( void ) { cout << "Base" << endl; }
};

struct Child : public Base
{
	virtual void Test( void ) { cout << "Child" << endl; }
};

Base::Base( const string& name )
{
	if( name == "Child" )
	{
		realloc( this, sizeof( Child ) );
		Child* c = new(this) Child;
	}
}

int main()
{
	Base* p = new Base( "Child" );
	p->Test();
	delete p;
}

но конечно тут нужно использовать фабрику и только фабрику

wota ★★
()

Правильно я понимаю, что мне нужно перегрузить operator new? Можно увидеть пример кода?

неправильно. Перезагрузка new нужна для своего распределителя, а в этом случае используй фабрики, как уже сказали.

я получал указатель на экземпляр класса Leds наследующего от Instance? Не хочется создавать protected переменную, в которой будет хранится переданный тип устройства, и в зависимости от её значения действовать по-разному.

зачем тут переменная?

drBatty ★★
()
Ответ на: комментарий от namezys

Где это ты видел, что new/delete что нить знает о классе?

почему это foo::new не знает? Знает. Вот ::new действительно не в курсе, правда конструктор всё равно запускает для нового нетривиального объекта.

drBatty ★★
()
Ответ на: комментарий от namezys

А realloc выделил по тому же месту?

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

А дестрктор?

с ним все будет ок, в данном примере я его не добавил для краткости

А еще всякой фигни?

какой?

В общем это полно UB

ты не знал, что так можно сделать - я показал, и написал, что так делать не стоит, все остальное - твои переживания

wota ★★
()
Ответ на: комментарий от wota

ты не знал, что так можно сделать - я показал, и написал, что так делать не стоит, все остальное - твои переживания

я вот таки знал (догадаться не сложно), но в продакшене такого я не применяю. хотя как-то пробовал, работает вроде...

drBatty ★★
()
Ответ на: комментарий от namezys

А если вызвать new у базового класса - то не знает.

это точно... Потому и не предлагаю.

drBatty ★★
()
Ответ на: комментарий от namezys

руки отрывать

за такое в рабочем коде - однозначно, а как пример возмозможности - сойдет

wota ★★
()

Как так может)

enum DEVICE
{
	iVALUE1 = 1,
	iVALUE2 = 2
};

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

};

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

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

A* func(DEVICE e)
{
	A*a = NULL;	

	switch(e){

	case iVALUE1:
		a = new B();
		break;

	case iVALUE2:
		a = new C();
		break;

	default:
		break;
	}

	return a;
}

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

ну а вообще есть так, то по хорошому нужно возвращать это через auto_ptr

Уважаемый эксперт, не объясните ли почему тогда в C++98 auto_ptr практически никто не использовал и не рекомендовал использовать, а в С++11 его объявили устаревшим?

Begemoth ★★★★★
()
Ответ на: комментарий от dmfd
Instance* createInstance(const string& name);

Я только замечу, что лучше возвращать std::unique_ptr<Instance> если не планируется совместное владение объектом: во-первых, по типу результата, видно что функция передает владение объектом вызвавшему коду, во-вторых использование auto достаточно для автоматического удаления объекта. Если же заранее известно, что владение объектом будет совместным, то можно возвращать std::shared_ptr и создавать объект std::make_shared.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

ну я думаю не мне тебе объяснять как он передает свои «полномочия»
а то что его никто не использовал это спорный вопрос, да в С++11 для домохозяек сделали более удобный интерфейс чем был у auto_ptr

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

ну я думаю не мне тебе объяснять как он передает свои «полномочия»

Да вот в том и дело, что от него больше проблем, чем пользы.

Begemoth ★★★★★
()
Ответ на: комментарий от Begemoth

да он не идеален, но иногда без него тоже плохо.
а вообще я по бусту больше тащусь)

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от wota

какой?

например, в Base могут быть не-POD члены с нетривиальныи конструкторами/деструкторами, тогда у тебя в лучшем случае потечет память, а в худшем вылезет segfault

Reset ★★★★★
()
Ответ на: комментарий от Reset

например, в Base могут быть не-POD члены с нетривиальныи конструкторами/деструкторами

ну это очевидно, и это будет логическая ошибка

wota ★★
()
Ответ на: комментарий от Reset

ну и кстати это обходится одной строкой

wota ★★
()
Ответ на: комментарий от Miguel

сериализация, например, но конечно не new Instance(«LED»), а просто фабрика по имени типа

wota ★★
()
Ответ на: комментарий от Begemoth

Да вот в том и дело, что от него больше проблем, чем пользы.

Достаточно кривыми руками можно и shared_ptr сломать. А auto_ptr хотя бы избавляет от радости писать delete - это уже многого стоит.

const86 ★★★★★
()
Ответ на: комментарий от korvin_

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

trex6 ★★★★★
()
Ответ на: комментарий от trex6

(не забудьте упомянуть название)

Как будто по аватарке непонятно.

Kosyak ★★★★
()
Ответ на: комментарий от trex6

Расскажите нам, сирым и убогим, как на вашем великом и могучем языке

data Уж = ... -- длина etc.
data Ёж = ... -- кол-во иголок etc.

class Device a where
    ... -- класс типа устройства

instance Device Уж where
    ... -- экземпляр класса типа для ужа

instance Device Ёж where
    ... -- экземпляр класса типа для ежа

device :: Device a => String -> a
device "Уж" = Уж ...
device "Ёж" = Ёж ...


Как-то так. Штангист 1-lvl

mix_mix ★★★★★
()
Ответ на: комментарий от trex6

Алсо, если ужей и ежей немного, то лучше даже не юзать тайп-классы делать как-то так:

data Device = Мотор | Светодиод | Уж | Ёж
    deriving (Read, Show, Eq)

deviceDoSmth :: Device -> Smth
deviceDoSmth Уж = ...
deviceDoSmth Ёж = ...
...

mix_mix ★★★★★
()
Ответ на: комментарий от mix_mix

мой вариант пишется примерно так же, с поправкой на if вместо паттерн-матчинга. и сдаётся мне, этот код не взлетит.

dmfd
()
Ответ на: комментарий от mix_mix

P.P.S. Как я и ванговал, не может вывести тип: http://ideone.com/9MAkJ

Это можно сделать как-то так, например:

{-# LANGUAGE ExistentialQuantification #-}
 
class Device a
 
data Device' = forall a.(Device a, Show a)=> MakeDevice' a
 
data Hedgehog = Hedgehog
        deriving (Show)
 
data GrassSnake = GrassSnake
        deriving (Show)
 
instance Device Hedgehog
instance Device GrassSnake
 
device :: String -> Device'
device "Уж" = MakeDevice' Hedgehog
device "Ёж" = MakeDevice' GrassSnake
 
main = putStrLn $ f $ device "Уж"
        where f (MakeDevice' a) = show a

http://ideone.com/0Syxq

Но это, имхо, ничуть не проще (я это только с пятой попытки правильно написал), чем

class Device {};
 
class Hedgehog : public Device
{};
 
class GrassSnake : public Device
{};
 
Device* makeDevice(const string& str)
{
  if(str=="Ёж") 
    return new Hedgehog();
  if(str=="Уж") 
    return new GrassSnake();
  return 0;
}

Нужно ли возвращать здесь smart pointer или raw pointer - вопрос спорный. Я бы возвращал именно raw pointer, а юзер пусть оборачивает его как хочет.

dmfd
()
Ответ на: комментарий от trex6

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

Про обход каких вещей речь?

korvin_ ★★★★★
()
Ответ на: комментарий от wota

ты не знал, что так можно сделать - я показал, и написал, что так делать не стоит,

Ты показал, что можешь писать код с фатальными ошибками (игнорирование возвращаемого значения realloc).

tailgunner ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.