2009-10-16 6 views
2

Je souhaite créer une méthode substr en C++ dans une classe de chaînes que j'ai créée.Méthode de sous-valeur l en C++

La classe de chaîne est basée sur la chaîne de style C bien sûr, et je prends soin de la gestion de la mémoire.

Je veux écrire une fonction substr(start, length) qui peut fonctionner sur la façon régulière:

CustomString mystring = "Hello"; 

cout << mystring.substr(0,2); // will print "He" 

Et aussi de cette façon:

mystring.substr(1,3) = "DD"; // mystring will be "HDDo" 

Notez que même si je reçois un 3 caractères de long sous -string, j'ai mis dans l'affectation seulement 2 caractères et la chaîne de sortie sera HDDo, encore.

Une idée de comment y arriver?

Merci!

+14

Juste ce dont C++ a besoin. * Une autre classe de chaînes. – jalf

+3

"... l'écriture de classes de chaînes est l'un des sports d'intérieur les plus populaires parmi les programmeurs C++." (P.J. Plauger, "Le projet de bibliothèque standard C++", p345) – sbi

+0

* I * a écrit une classe de liste liée il y a un an ou deux. Parce que je savais que C++ * avait besoin d'une autre classe LL. –

Répondre

6

Pour cela, vous devrez probablement écrire votre substr() pour renvoyer un objet proxy qui garde la trace de la partie de la chaîne d'origine à laquelle il est fait référence. L'objet proxy surchargera operator= et remplacera la sous-chaîne référencée par la nouvelle sous-chaîne.

Modifier en réponse à des commentaires: l'idée d'un proxy est qu'il est assez similaire à la classe pour laquelle il est un proxy que renvoyer un proxy est toujours une opération fermée - du point de vue de l'utilisateur, tout ce qui est visible est le type d'objet original, mais il a des capacités qui ne seraient pas possibles (ou seraient beaucoup plus difficiles à implémenter) sans le proxy. Dans ce cas, nous la classe proxy serait privée à la classe de chaîne, de sorte que l'utilisateur ne pourrait jamais créer une instance de la classe proxy, sauf en tant que temporaire. Ce temporaire peut être utilisé pour modifier sa chaîne parent si vous lui attribuez. L'utilisation du proxy de toute autre manière donne simplement une chaîne. Pour ce que cela vous achète en essayant de tout faire à l'intérieur de la chaîne d'origine: chaque objet proxy est un objet temporaire - le compilateur peut/will/ne garder trace de la façon de créer des temporaires si nécessaire, les détruit correctement à la fin d'une expression complète, etc. Le compilateur conserve également la trace de la sous-chaîne à laquelle une assignation particulière fait référence, la convertit automatiquement en chaîne lorsque nous essayons d'utiliser sa valeur, et ainsi de suite. Autrement dit, le compilateur gère presque tout le dur labeur impliqué.

Voici quelques codes de travail. La classe de chaînes qui l'entoure est assez minime (par exemple, elle n'a aucune capacité de recherche). Je m'attendrais à ajouter un montant raisonnable à une version utile de la classe de chaînes. La classe de proxy, cependant, est complète - je ne m'attendrais pas à voir cela changer beaucoup (le cas échéant) dans une version complète de la classe de chaîne.

#include <vector> 
#include <algorithm> 
#include <iostream> 
#include <iterator> 

class string { 
    std::vector<char> data; 
public: 
    string(char const *init) { 
     data.clear(); 
     data.assign(init, init+strlen(init)); 
    } 

    string(string const &s, size_t pos, size_t len) { 
     data.assign(s.data.begin()+pos, s.data.begin()+pos+len); 
    } 

    friend class proxy; 

    class proxy { 
     string &parent; 
     size_t pos; 
     size_t length; 
    public: 
     proxy(string &s, size_t start, size_t len) : parent(s), pos(start), length(len) {} 

     operator string() { return string(parent, pos, length); } 

     proxy &operator=(string const &val) { 
      parent.data.erase(parent.data.begin()+pos, parent.data.begin()+pos+length); 
      parent.data.insert(parent.data.begin()+pos, val.data.begin(), val.data.end()); 
      return *this; 
     } 
    }; 

    proxy substr(size_t start, size_t len) { 
     return proxy(*this, start, len); 
    } 

    friend std::ostream &operator<<(std::ostream &os, string const &s) { 
     std::copy(s.data.begin(), s.data.end(), std::ostream_iterator<char>(os)); 
     return os; 
    } 
}; 

#ifdef TEST 

int main() { 
    string x("Hello"); 

    std::cout << x << std::endl; 

    std::cout << x.substr(2, 3) << std::endl; 

    x.substr(2, 3) = "DD"; 

    std::cout << x << std::endl; 

    return 0; 
} 

#endif 

Edit 2: En ce qui concerne les sous-chaînes de sous-chaînes vont, ça dépend. La situation qui n'est pas actuellement couverte est si vous souhaitez affecter à une sous-chaîne d'une sous-chaîne, et qu'elle affecte la chaîne d'origine. Si vous voulez quelque chose comme x=y.substr(1,4).substr(1,2); cela fonctionnera bien tel quel. Le premier proxy sera converti en une chaîne, et le second sous-réseau sera appelé sur cette chaîne.

Si vous le souhaitez: x.substr(1,4).substr(1,2) = "whatever"; cela ne fonctionnera pas actuellement.Je ne suis pas sûr qu'il accomplit beaucoup, mais en supposant qu'il fait, l'ajout de soutien, il est assez minime - vous souhaitez ajouter un membre substr à proxy:

proxy substr(size_t start, size_t len) { 
    return proxy(parent, pos+start, len); 
} 
+0

Mais que faire si je veux une sous-chaîne d'une sous-chaîne? –

+0

@Earwicker: Alors 'proxy :: operator string()' devrait être lancé. – sbi

+0

@sbi - peut-être que ça devrait être le cas, mais ça ne sera pas en C++ voir la mise à jour de Jerry. Je pense que Jerry a raison - sous cette forme, cela ne fait pas grand-chose; il serait préférable que nous puissions traiter les chaînes de façon uniforme (quelle que soit leur implémentation), au lieu d'avoir deux types pour représenter les chaînes. Sinon, je ne peux pas passer une sous-chaîne pour annuler 'foo (string & s)' pour laisser 'foo' le modifier. Je devrais écrire toutes ces fonctions comme des modèles, qui (sur la plupart des plates-formes) pousse le code dans les fichiers d'en-tête. –

0

On peut supposer que vous voulez revenir substr une chaîne, plutôt qu'une autre classe de proxy. Vous devez donc rendre votre classe de chaînes capable de contenir un pointeur vers sa propre copie des données de chaîne et également un pointeur vers un autre objet chaîne à partir duquel elle a été créée (comme valeur de retour de substr), avec des informations sur partie de la chaîne à partir de laquelle il a été créé.

Cela peut devenir assez compliqué lorsque vous appelez substr sur une chaîne renvoyée par un autre appel à substr.

La complexité ne vaut probablement pas l'attractivité de l'interface.

+0

Si une chaîne en référence une autre, qu'est-ce que c'est si ce n'est pas un objet proxy? Et étant donné la complexité que vous décrivez, qu'est-ce qui vous achèterait de l'idée de proxy de Jerry? – sbi

+0

Le fait est que la prise d'une sous-chaîne devrait être une opération fermée - elle devrait renvoyer un autre objet du même type, pas * une autre * classe de proxy. c'est-à-dire que la chaîne elle-même servirait de proxy. En outre, la même complexité existerait évidemment dans * toute * classe de proxy pleinement capable. –

+0

Cela donnera des problèmes pour ce cas: CustomString mystring = "Bonjour"; CustomString mysub = mystring.substr (1,3); mysub = "DD"; Vraisemblablement, vous voudriez que l'assignation à mysub rompe la connexion avec le parent. –

0

La première exigence est simple; rechercher l'implémentation standard de l'opérateur.

Librement, c_string& substr(int, int)

La deuxième partie, non pas tant, je ne pense pas. Ça va ressembler, je crois. Cependant, je vais y réfléchir et vous revenir au cours du week-end.