Вы можете использовать /e
, модифицированный для выполнения кода (для каждого совпадения отдельно) для генерации строки замены:
use strict;
use warnings;
my $start_regex = qr/a.*?b/;
my $end_regex = qr/c.*?d/;
my $string = 'a1b x c2d x a345b qqxxxc678d xx abxcd';
$string =~ s/($start_regex)(.*?)($end_regex)/ my ($start_match, $middle_match, $end_match) = ($1, $2, $3); $middle_match =~ s!x!y!g; $start_match . $middle_match . $end_match /eg;
print $string, "\n";
Распечатает
a1b y c2d x a345b qqyyyc678d xx abycd
Обратите внимание на альтернативу разделитель регулярных выражений s!!!
во внутреннем регулярном выражении.
Важно хранить $1
, $2
и $3
внешнего соответствия регулярному выражению во временных переменных, так как они будут потеряны, когда вы выполните другое регулярное выражение в коде замены.
При написании класса C ++ уместно подумать, будет ли он
Тип значения
Копирование по значению, идентичность никогда не важна. Для него уместно быть ключом в std :: map. Например, класс "string" или класс "date" или класс "complex number". «Копировать» экземпляры такого класса имеет смысл.
Тип сущности
Идентичность важна. Всегда передается по ссылке, никогда не по значению. Часто вообще не имеет смысла «копировать» экземпляры класса. Когда это имеет смысл, полиморфный метод «клонирования» обычно более уместен. Примеры: класс Socket, класс Database, класс «policy», все, что будет «замыканием» на функциональном языке.
И pImpl, и чистый абстрактный базовый класс - это методы для уменьшения зависимости времени компиляции.
Однако я всегда использую pImpl только для реализации типов Value (тип 1), и только иногда, когда я действительно хочу минимизировать связывание и компиляцию. временные зависимости. Часто это не стоит беспокоиться. Как вы правильно заметили, есть больше синтаксических издержек, потому что вы должны писать методы пересылки для всех открытых методов. Для классов типа 2 я всегда использую чистый абстрактный базовый класс с соответствующими фабричными методами.
более синтаксические издержки, потому что вы должны написать методы пересылки для всех открытых методов. Для классов типа 2 я всегда использую чистый абстрактный базовый класс с соответствующими фабричными методами. более синтаксические издержки, потому что вы должны написать методы пересылки для всех открытых методов. Для классов типа 2 я всегда использую чистый абстрактный базовый класс с соответствующими фабричными методами. Указатель на реализацию
, как правило, скрывает детали структурной реализации. Интерфейсы
предназначены для реализации различных реализаций. Они действительно служат двум различным целям.
В моем понимании эти две вещи служат совершенно различным целям. Цель идиомы pimple состоит в том, чтобы дать вам представление о вашей реализации, чтобы вы могли делать такие вещи, как быстрые замены для сортировки.
Цель виртуальных классов заключается в том, чтобы допустить полиморфизм, т. Е. У вас есть неизвестный указатель к объекту производного типа, и когда вы вызываете функцию x, вы всегда получаете правильную функцию для любого класса, на который в действительности указывает базовый указатель.
Яблоки и апельсины действительно.
Идиома pimpl помогает вам сократить зависимости и время сборки, особенно в больших приложениях, и сводит к минимуму воздействие заголовка деталей реализации вашего класса на один модуль компиляции. Пользователям вашего класса даже не нужно знать о существовании прыщика (за исключением загадочного указателя, к которому они не относятся!).
Абстрактные классы (чистые виртуалы) - это то, о чем должны знать ваши клиенты: если вы пытаетесь использовать их для уменьшения связности и циклических ссылок, вам необходимо добавить некоторый способ, позволяющий им создавать ваши объекты (например, с помощью фабричных методов). или классы, внедрение зависимости или другие механизмы).
Существует очень реальная проблема с разделяемыми библиотеками, которую идиома pimpl обходит аккуратно, чего не могут чистые виртуалы: вы не можете безопасно изменять / удалять элементы данных членов класс, не заставляя пользователей класса перекомпилировать их код. Это может быть приемлемо при некоторых обстоятельствах, но не, например, для системных библиотек.
Чтобы подробно объяснить проблему, рассмотрите следующий код в вашей общей библиотеке / заголовке:
// header
struct A
{
public:
A();
// more public interface, some of which uses the int below
private:
int a;
};
// library
A::A()
: a(0)
{}
Компилятор генерирует код в разделяемой библиотеке, который вычисляет адрес целого числа, которое должно быть инициализировано, чтобы иметь определенное смещение (вероятно, ноль в этом случае, потому что это единственный член) от указателя на объект A, который он знает как this
.
На стороне пользователя кода новый A
сначала выделит sizeof (A)
байтов памяти, затем передаст указатель на эта память для конструктора A :: A ()
как , это
.
Если в более поздней ревизии вашей библиотеки вы решили удалить целое число, сделайте его больше, меньше, или добавление членов, будет несоответствие между количеством выделяемого пользователем кода памяти и смещениями, ожидаемыми кодом конструктора. Вероятный результат - сбой, если вам повезет - если вам повезет меньше, ваше программное обеспечение ведет себя странно.
Я искал ответ на тот же вопрос. После прочтения некоторых статей и некоторой практики я предпочитаю использовать "Чистые виртуальные интерфейсы классов" .
Единственный недостаток (я пытаюсь исследовать это) в том, что идиома pimpl могла бы быть быстрее
Мы не должны забывать, что наследование сильнее, ближе сцепление, чем делегирование. Я бы также принял во внимание все вопросы, поднятые в ответах, при принятии решения, какие идиомы дизайна использовать при решении конкретной проблемы.